From c5f181509ed6f077d4bb7382df2af8c6f6eebc61 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 09:40:10 -0400 Subject: [PATCH 01/73] MAINT: added .DS_Store to gitignore Updated .gitignore file for Mac systems. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 233bbec0..8baa02a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Autosaves *~ +.DS_Store # Compiled python modules. *.py[cod] From 107d3053ff5f34175a10e6756cc3070baf8e94e6 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 09:43:06 -0400 Subject: [PATCH 02/73] MAINT: updated dep warnings Updated deprecation warnings with a version, addressing issue #109. --- ocbpy/boundaries/dmsp_ssj_files.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ocbpy/boundaries/dmsp_ssj_files.py b/ocbpy/boundaries/dmsp_ssj_files.py index 4caacd75..7f38aea9 100644 --- a/ocbpy/boundaries/dmsp_ssj_files.py +++ b/ocbpy/boundaries/dmsp_ssj_files.py @@ -82,11 +82,10 @@ def fetch_ssj_files(stime, etime, out_dir=None, sat_nums=None): requests.exceptions.ProxyError """ - # TODO(#109): add version for removal warnings.warn("".join(["ssj_auroral_boundaries package is no longer ", "supported; use `fetch_ssj_boundary_files` to ", "access the boundary Zenodo archive. This function", - " will be removed in version 0.X.X+."]), + " will be removed in version 0.4.1+."]), DeprecationWarning, stacklevel=2) # Get and test the output directory @@ -184,11 +183,10 @@ def create_ssj_boundary_files(cdf_files, out_dir=None, `fetch_ssj_boundary_files` """ - # TODO(#109): add version for removal warnings.warn("".join(["ssj_auroral_boundaries package is no longer ", "supported; use `fetch_ssj_boundary_files` to ", "access the boundary Zenodo archive. This function", - " will be removed in version 0.X.X+."]), + " will be removed in version 0.4.1+."]), DeprecationWarning, stacklevel=2) # Test the directory inputs @@ -673,11 +671,10 @@ def fetch_format_ssj_boundary_files(stime, etime, out_dir=None, rm_temp=True, if 'use_dep' in kwargs.keys(): use_dep = kwargs['use_dep'] - # TODO(#109): add version for removal warnings.warn("".join(["ssj_auroral_boundaries package is no longer ", "supported. Temporary support of `use_dep` ", "kwarg to access deprecated routines will be " - "removed in version 0.X.X+."]), + "removed in version 0.4.1+."]), DeprecationWarning, stacklevel=2) if use_dep: From 9e6b4373d9780c21af61ec3a2a114386e354f8de Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 09:54:49 -0400 Subject: [PATCH 03/73] MAINT: updated pyproject.toml Updated pyproject.toml and deprecated the setup.py file. --- pyproject.toml | 29 ++++++++++++++++++- setup.cfg | 77 +------------------------------------------------- setup.py | 11 -------- 3 files changed, 29 insertions(+), 88 deletions(-) delete mode 100644 setup.py diff --git a/pyproject.toml b/pyproject.toml index ed98c7a9..b4981439 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,8 @@ name = "ocbpy" license = {file = "LICENSE"} description = 'Location relative to open/closed field line boundary' maintainers = [ - {name = "Angeline G. Burrell", email = "angeline.burrell@nrl.navy.mil"}, + {name = "Angeline G. Burrell", + email = "angeline.g.burrell.civ@us.navy.mil"}, ] requires-python = ">=3.7" dependencies = [ @@ -49,6 +50,7 @@ classifiers = [ "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", 'Operating System :: Unix', 'Operating System :: POSIX', 'Operating System :: POSIX :: Linux', @@ -67,6 +69,7 @@ doc = [ "pysat", "sphinx>=1.3", "sphinx-rtd-theme", + "numpydoc", ] [project.urls] @@ -74,3 +77,27 @@ source = "https://github.com/aburrell/ocbpy" documentation = "https://ocbpy.readthedocs.io/en/latest/" tracker = "https://github.com/aburrell/ocbpy/issues" download = "https://github.com/aburrell/ocbpy/releases" + + +[tool.coverage.run] +relative_files = True +include = */ocbpy/* + */ocbpy/tests/* + +[tool.coverage.paths] +ocb_paths = ocbpy/ + */lib/*/site-packages/ocbpy + +[tool.coverage.report] +omit = */lib/*/site-packages/*.py + */lib/*/site-packages/a*/* + */lib/*/site-packages/c*/* + */lib/*/site-packages/d*/* + */lib/*/site-packages/g*/* + */lib/*/site-packages/i*/* + */lib/*/site-packages/m*/* + */lib/*/site-packages/n*/* + */lib/*/site-packages/p*/* + */lib/*/site-packages/r*/* + */lib/*/site-packages/s*/* + */lib/*/site-packages/u*/* diff --git a/setup.cfg b/setup.cfg index 52ed7351..8b8742d6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,84 +1,9 @@ [metadata] name = ocbpy version = 0.3.0 -url = https://github.com/aburrell/ocbpy -author = Angeline G. Burrell, et al. -author_email = angeline.burrell@nrl.navy.mil -description = 'Location relative to open/closed field line boundary' -keywords = - coordinates - field-line boundary - auroral oval - polar cap - pysat - ionosphere - atmosphere - thermosphere - magnetosphere - heliosphere - observations - models - space - satellites - analysis -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - Topic :: Scientific/Engineering :: Physics - Topic :: Scientific/Engineering :: Atmospheric Science - License :: OSI Approved :: BSD License - Natural Language :: English - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.7 - Programming Language :: Python :: 3.8 - Programming Language :: Python :: 3.9 - Programming Language :: Python :: 3.10 - Operating System :: MacOS :: MacOS X - Operating System :: Microsoft :: Windows - Operating System :: POSIX :: Linux -license_file = LICENSE -long_description = file: README.md -long_description_content_type = text/markdown - -[options] -python_requires = >= 3.5 -setup_requires = setuptools >= 38.6; pip >= 10 -include_package_data = True -zip_safe = False -packages = find: -install_requires = aacgmv2 - numpy - -[options.extras_require] -pysat_instruments = pysat -dmsp_ssj = ssj_auroral_boundaries - zenodo_get [flake8] max-line-length = 80 ignore = E275 - W503 - -[coverage:run] -relative_files = True -include = */ocbpy/* - */ocbpy/tests/* - -[coverage:paths] -ocb_paths = ocbpy/ - */lib/*/site-packages/ocbpy - -[coverage:report] -omit = */lib/*/site-packages/*.py - */lib/*/site-packages/a*/* - */lib/*/site-packages/c*/* - */lib/*/site-packages/d*/* - */lib/*/site-packages/g*/* - */lib/*/site-packages/i*/* - */lib/*/site-packages/m*/* - */lib/*/site-packages/n*/* - */lib/*/site-packages/p*/* - */lib/*/site-packages/r*/* - */lib/*/site-packages/s*/* - */lib/*/site-packages/u*/* + W503 \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 0adf8301..00000000 --- a/setup.py +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# Copyright (C) 2017, AGB & GC -# Full license can be found in License.md -# ----------------------------------------------------------------------------- - -from setuptools import setup - -# Run setup, with package metadata stored in setup.cfg - -setup() From df5da602aac52ce3233e036a459ff5c960aff06e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 10:13:05 -0400 Subject: [PATCH 04/73] ENH: updated pyproject.toml Added test dependencies to pyproject.toml and added version capts to pysat. --- pyproject.toml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b4981439..cc4242ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -60,17 +60,23 @@ classifiers = [ dynamic = ['version'] [project.optional-dependencies] -pysat_instruments = [ "pysat" ] +pysat_instruments = [ "pysat>=3.0.1" ] dmsp_ssj = [ "ssj_auroral_boundaries", "zenodo_get", ] doc = [ - "pysat", + "pysat>=3.0.1", "sphinx>=1.3", "sphinx-rtd-theme", "numpydoc", ] +test = [ + "coverage", + "coveralls", + "flake8", + "packaging", +] [project.urls] source = "https://github.com/aburrell/ocbpy" From c4b47002dc3f933bf8a33ab58514992c4b4f4634 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 10:13:40 -0400 Subject: [PATCH 05/73] TST: added test for release candidate Added a CI test for the ocbpy Release Candidate. --- .github/workflows/pip_rc_install.yml | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/pip_rc_install.yml diff --git a/.github/workflows/pip_rc_install.yml b/.github/workflows/pip_rc_install.yml new file mode 100644 index 00000000..1124b9ba --- /dev/null +++ b/.github/workflows/pip_rc_install.yml @@ -0,0 +1,36 @@ +# This workflow will install Python dependencies and the latest RC of ocbpy from +# Test PyPi. This test should be manually run before a Release Candidate is +# officially approved and versioned. For more information see: +# https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions + +name: Test install of latest RC from pip + +on: [workflow_dispatch] + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: ["ubuntu-latest", "macos-latest", "windows-latest"] + python-version: ["3.11"] # Keep this version at the highest supported Python version + + name: Python ${{ matrix.python-version }} on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install standard dependencies + run: pip install -r requirements.txt + + - name: Install pysat RC + run: pip install --no-deps --pre -i https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ ocbpy + + - name: Check that installation imports correctly + run: | + cd .. + python -c "import ocbpy; print(ocbpy.__version__)" From 2f6fb7a9eadefb183805c4c6c8d6a87f41545e1f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 10:23:04 -0400 Subject: [PATCH 06/73] TST: updated CI tests Updated the CI tests by: - updating the GitHub Actions versions, - use the pyproject.toml for installation, - add a new python test version, and - change the configuration file to pyproject.toml. --- .github/workflows/docs.yml | 13 ++++++------- .github/workflows/main.yml | 24 +++++++++++------------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index f699a536..61a5ec25 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,28 +11,27 @@ on: [push, pull_request] jobs: build: - runs-on: ubuntu-latest + runs-on: ["ubuntu-latest"] strategy: fail-fast: false matrix: - python-version: ["3.9"] + python-version: ["3.11"] name: Documentation tests steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Set CDF Lib environment variable run: echo "CDF_LIB=$HOME/lib" >> $GITHUB_ENV - - name: Install standard and test dependencies, then ocbpy + - name: Install dependencies run: | - pip install -r docs/requirements.txt sphinx sphinx_rtd_theme numpydoc + pip install .[doc] bash requirements.extra 2 - python setup.py install - name: Check documentation build run: sphinx-build -E -b html docs dist/docs diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 0cc63140..9c5794e4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,8 +13,8 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] - python-version: ["3.7", "3.8", "3.9", "3.10"] - install-extras: [0, 1, 2] + python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + install-extras: [0, 1, 2] # TODO(#129): update to replace extra flag exclude: - os: windows-latest install-extras: 2 @@ -22,9 +22,9 @@ jobs: name: Python ${{ matrix.python-version }} on ${{ matrix.os }} with extras ${{ matrix.install-extras }} runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -37,16 +37,14 @@ jobs: env: CDF_LIB: $HOME/lib run: | - pip install -r requirements.txt + pip install .[test] bash requirements.extra ${{ matrix.install-extras }} - python setup.py install - name: Install standard and test dependencies, then ocbpy without SSJ if: ${{ matrix.install-extras != 2 }} run: | - pip install -r requirements.txt + pip install .[test] bash requirements.extra ${{ matrix.install-extras }} - python setup.py install - name: Test PEP8 compliance run: flake8 . --count --show-source --statistics @@ -55,25 +53,25 @@ jobs: run: flake8 . --count --exit-zero --max-complexity=10 --statistics - name: Run unit and integration tests - run: coverage run --rcfile=setup.cfg -m unittest discover + run: coverage run --rcfile=pyproject.toml -m unittest discover - name: Publish results to coveralls upon success env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.install-extras }} - run: coveralls --rcfile=setup.cfg --service=github || true + run: coveralls --rcfile=pyproject.toml --service=github || true finish: needs: build runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.10"] + python-version: ["3.11"] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Coveralls Finished From 0dbc5aaa9cc5e50b9f7d44df3747036ae9bd66bc Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 17:16:59 -0400 Subject: [PATCH 07/73] MAINT: update version number access Update the package init file to obtain the version number from the pyproject.toml file. --- ocbpy/__init__.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ocbpy/__init__.py b/ocbpy/__init__.py index 907b6781..0f7fbaea 100644 --- a/ocbpy/__init__.py +++ b/ocbpy/__init__.py @@ -5,9 +5,14 @@ # ---------------------------------------------------------------------------- """Auroral oval and polar cap normalised location calculation tools.""" -# Define a logger object to allow easier log handling import logging +try: + from importlib import metadata +except ImportError: + import importlib_metadata as metadata + +# Define a logger object to allow easier log handling logging.raiseExceptions = False logger = logging.getLogger('ocbpy_logger') @@ -27,4 +32,4 @@ from ocbpy.cycle_boundary import match_data_ocb # noqa F401 # Define the global variables -__version__ = str('0.3.0') +__version__ = metadata.version('ocbpy') From 5fe8855d53b82ee154f50d0f34d20ef303eac865 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 17:17:41 -0400 Subject: [PATCH 08/73] STY: update boundary directory Update the boundary directory discovery to use pathlib, since resources do not support directories. --- ocbpy/boundaries/files.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ocbpy/boundaries/files.py b/ocbpy/boundaries/files.py index f544b1b6..ac1ccec6 100644 --- a/ocbpy/boundaries/files.py +++ b/ocbpy/boundaries/files.py @@ -8,6 +8,7 @@ import datetime as dt import itertools import os +import pathlib import ocbpy @@ -22,7 +23,8 @@ def get_boundary_directory(): """ - boundary_dir = os.path.join(os.path.dirname(ocbpy.__file__), "boundaries") + boundary_dir = os.path.join(str(pathlib.Path( + ocbpy.boundaries.__file__).resolve().parent)) if not os.path.isdir(boundary_dir): raise OSError("can't find the OCBpy boundary file directory") From fba385afe80b87e79c1b2a62a7f9d9860406295c Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 17:18:32 -0400 Subject: [PATCH 09/73] ENH: update SuperMAG file reading Add a data catch for reading SuperMAG files without data and fix the docstring to reflect the correct outputs. --- ocbpy/instruments/supermag.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ocbpy/instruments/supermag.py b/ocbpy/instruments/supermag.py index b022dd64..3699cd7e 100644 --- a/ocbpy/instruments/supermag.py +++ b/ocbpy/instruments/supermag.py @@ -210,6 +210,8 @@ def load_supermag_ascii_data(filename): Returns ------- + header : list + List of strings containing the header out : dict The dict keys are specified by the header data line, the data for each key are stored in the numpy array @@ -282,6 +284,13 @@ def load_supermag_ascii_data(filename): n = -1 ind = {"SMU": fill_val, "SML": fill_val} + # If no data was found, this may not be the correct type of file + if hflag: + ocbpy.logger.error( + "no data in the SuperMAG file '{:}', check format.".format( + filename)) + return header, dict() + # Recast data as numpy arrays and replace fill value with np.nan for k in out: if k == "STID": From 987301e9f35a71b24833dd309a9cb28d6a6d0281 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 17:18:50 -0400 Subject: [PATCH 10/73] DOC: added missing docstring Added a docstring to the test init file. --- ocbpy/tests/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ocbpy/tests/__init__.py b/ocbpy/tests/__init__.py index e69de29b..a25dd91e 100644 --- a/ocbpy/tests/__init__.py +++ b/ocbpy/tests/__init__.py @@ -0,0 +1 @@ +"""Initialisation file for unit and integration tests.""" From 9881f45e2d34edd142940f01f33e3f1c31305ea4 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 17:37:16 -0400 Subject: [PATCH 11/73] TST: created a common class for tests Created a common class for logging tests and defined the test data directory as a variable. --- ocbpy/tests/class_common.py | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 ocbpy/tests/class_common.py diff --git a/ocbpy/tests/class_common.py b/ocbpy/tests/class_common.py new file mode 100644 index 00000000..5c5017c0 --- /dev/null +++ b/ocbpy/tests/class_common.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2017, AGB +# Full license can be found in License.md +# ----------------------------------------------------------------------------- +"""Common test classes and variables.""" + +from io import StringIO +import logging +import os +import pathlib +import unittest + +import ocbpy + +test_dir = os.path.join(str(pathlib.Path( + ocbpy.tests.__file__).resolve().parent), "test_data") + + +class TestLogWarnings(unittest.TestCase): + """Unit tests for logging warnings.""" + + def setUp(self): + """Initialize the logging test environment.""" + + self.lwarn = u'' + self.lout = u'' + self.log_capture = StringIO() + ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) + ocbpy.logger.setLevel(logging.WARNING) + return + + def tearDown(self): + """Tear down the logging test environment.""" + + del self.lwarn, self.lout, self.log_capture + return + + def eval_logging_message(self): + """Evaluate the logging message.""" + + # Test logging error message and data output + self.lout = self.log_capture.getvalue() + self.assertTrue(self.lout.find(self.lwarn) >= 0) + return From 106e920c0762835a3ba0cc32d0749424fa516ff5 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 17:37:57 -0400 Subject: [PATCH 12/73] TST: simplified vorticity tests Simplified vorticity tests by using common classes and variables. --- ocbpy/tests/test_vort.py | 82 ++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 50 deletions(-) diff --git a/ocbpy/tests/test_vort.py b/ocbpy/tests/test_vort.py index 349e3392..38c8007a 100644 --- a/ocbpy/tests/test_vort.py +++ b/ocbpy/tests/test_vort.py @@ -7,37 +7,29 @@ import datetime as dt import filecmp -from io import StringIO -import logging import numpy as np import os import unittest - import ocbpy import ocbpy.instruments.vort as ocb_ivort +from ocbpy.tests import class_common as cc -class TestVortLogWarnings(unittest.TestCase): +class TestVortLogWarnings(cc.TestLogWarnings): """Unit tests for the vorticity instrument logging warnings.""" def setUp(self): """Initialize the test environment.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_file = os.path.join(self.ocb_dir, "tests", "test_data", - "test_vort") - self.test_ocb = os.path.join(self.ocb_dir, "tests", "test_data", - "test_north_circle") - self.temp_output = os.path.join(self.ocb_dir, "tests", "test_data", - "temp_vort") - self.assertTrue(os.path.isfile(self.test_file)) + super().setUp() + + self.test_file = os.path.join(cc.test_dir, "test_vort") + self.test_ocb = os.path.join(cc.test_dir, "test_north_circle") + self.temp_output = os.path.join(cc.test_dir, "temp_vort") + self.assertTrue(os.path.isfile(self.test_file), + msg="'{:}' is not a file".format(self.test_file)) - self.lwarn = u'' - self.lout = u'' - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) return def tearDown(self): @@ -45,8 +37,9 @@ def tearDown(self): if os.path.isfile(self.temp_output): os.remove(self.temp_output) - del self.test_file, self.temp_output, self.test_ocb, self.ocb_dir - del self.lwarn, self.lout, self.log_capture + del self.test_file, self.temp_output, self.test_ocb + + super().tearDown() return def test_vort2ascii_ocb_wrong_hemi(self): @@ -56,10 +49,9 @@ def test_vort2ascii_ocb_wrong_hemi(self): ocb_ivort.vort2ascii_ocb(self.test_file, self.temp_output, ocbfile=self.test_ocb, instrument='image', hemisphere=-1) - self.lout = self.log_capture.getvalue() # Test logging error message - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() return def test_vort_unexpected_line(self): @@ -81,10 +73,11 @@ def test_vort_unexpected_line(self): # Load the bad file data = ocb_ivort.load_vorticity_ascii_data(self.temp_output) - self.lout = self.log_capture.getvalue() - # Test logging error message and data output - self.assertTrue(self.lout.find(self.lwarn) >= 0) + # Test logging error message + self.eval_logging_message() + + # Test the data output self.assertIsNone(data) return @@ -95,33 +88,23 @@ class TestVort2AsciiMethods(unittest.TestCase): def setUp(self): """Initialize the testing set up.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_ocb = os.path.join(self.ocb_dir, "tests", "test_data", - "test_north_circle") - self.test_eab = os.path.join(self.ocb_dir, "tests", "test_data", - "test_north_eab") - self.test_file = os.path.join(self.ocb_dir, "tests", "test_data", - "test_hemi_vort") - self.test_eq_file = os.path.join(self.ocb_dir, "tests", "test_data", - "test_eq_hemi_vort") - self.test_empty = os.path.join(self.ocb_dir, "tests", "test_data", - "test_empty") - self.test_output_north = os.path.join(self.ocb_dir, "tests", - "test_data", "out_vort") - self.test_unscaled_north = os.path.join(self.ocb_dir, "tests", - "test_data", + self.test_ocb = os.path.join(cc.test_dir, "test_north_circle") + self.test_eab = os.path.join(cc.test_dir, "test_north_eab") + self.test_file = os.path.join(cc.test_dir, "test_hemi_vort") + self.test_eq_file = os.path.join(cc.test_dir, "test_eq_hemi_vort") + self.test_empty = os.path.join(cc.test_dir, "test_empty") + self.test_output_north = os.path.join(cc.test_dir, "out_vort") + self.test_unscaled_north = os.path.join(cc.test_dir, "out_vort_unscaled") - self.test_output_dual = os.path.join(self.ocb_dir, "tests", - "test_data", "out_dual_vort") - self.test_output_south = os.path.join(self.ocb_dir, "tests", - "test_data", "out_south_vort") - self.temp_output = os.path.join(self.ocb_dir, "tests", "test_data", - "temp_vort") + self.test_output_dual = os.path.join(cc.test_dir, "out_dual_vort") + self.test_output_south = os.path.join(cc.test_dir, "out_south_vort") + self.temp_output = os.path.join(cc.test_dir, "temp_vort") self.test_vals = {'CENTRE_MLAT': 67.27, 'DAY': 5, 'MLT': 3.127, 'UTH': 13.65, 'VORTICITY': 0.0020967, 'YEAR': 2000, 'DATETIME': dt.datetime(2000, 5, 5, 13, 39, 00), 'MONTH': 5} - self.assertTrue(os.path.isfile(self.test_file)) + self.assertTrue(os.path.isfile(self.test_file), + msg="'{:}' is not a file".format(self.test_file)) return def tearDown(self): @@ -129,10 +112,9 @@ def tearDown(self): if os.path.isfile(self.temp_output): os.remove(self.temp_output) - del self.test_file, self.temp_output, self.test_ocb, self.ocb_dir - del self.test_output_north, self.test_output_south, self.test_eq_file - del self.test_empty, self.test_eab, self.test_output_dual - del self.test_unscaled_north + del self.test_file, self.temp_output, self.test_ocb, self.test_eq_file + del self.test_output_north, self.test_output_south, self.test_eab + del self.test_empty, self.test_output_dual, self.test_unscaled_north return def test_vort2ascii_ocb(self): From 54e0e78343a5749f22d25569205259070b63fd41 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 25 Mar 2024 17:38:19 -0400 Subject: [PATCH 13/73] TST: simplified SuperMAG tests Simplified SuperMAG tests by using common classes and variables. --- ocbpy/tests/test_supermag.py | 51 +++++++++++++++++------------------- 1 file changed, 24 insertions(+), 27 deletions(-) diff --git a/ocbpy/tests/test_supermag.py b/ocbpy/tests/test_supermag.py index c12948f4..d8ed0774 100644 --- a/ocbpy/tests/test_supermag.py +++ b/ocbpy/tests/test_supermag.py @@ -12,6 +12,7 @@ import ocbpy import ocbpy.instruments.supermag as ocb_ismag from ocbpy.instruments import general +from ocbpy.tests import class_common as cc class TestSuperMAG2AsciiMethods(unittest.TestCase): @@ -20,26 +21,16 @@ class TestSuperMAG2AsciiMethods(unittest.TestCase): def setUp(self): """Initialize the setup for SuperMAG processing unit tests.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_ocb = os.path.join(self.ocb_dir, "tests", "test_data", - "test_north_circle") - self.test_eab = os.path.join(self.ocb_dir, "tests", "test_data", - "test_north_eab") - self.test_file = os.path.join(self.ocb_dir, "tests", "test_data", - "test_hemi_smag") - self.test_eq_file = os.path.join(self.ocb_dir, "tests", "test_data", - "test_eq_smag") - self.test_output_north = os.path.join(self.ocb_dir, "tests", - "test_data", "out_smag") - self.test_unscaled_north = os.path.join(self.ocb_dir, "tests", - "test_data", + self.test_ocb = os.path.join(cc.test_dir, "test_north_circle") + self.test_eab = os.path.join(cc.test_dir, "test_north_eab") + self.test_file = os.path.join(cc.test_dir, "test_hemi_smag") + self.test_eq_file = os.path.join(cc.test_dir, "test_eq_smag") + self.test_output_north = os.path.join(cc.test_dir, "out_smag") + self.test_unscaled_north = os.path.join(cc.test_dir, "out_smag_unscaled") - self.test_output_dual = os.path.join(self.ocb_dir, "tests", - "test_data", "out_dual_smag") - self.test_output_south = os.path.join(self.ocb_dir, "tests", - "test_data", "out_south_smag") - self.temp_output = os.path.join(self.ocb_dir, "tests", "test_data", - "temp_smag") + self.test_output_dual = os.path.join(cc.test_dir, "out_dual_smag") + self.test_output_south = os.path.join(cc.test_dir, "out_south_smag") + self.temp_output = os.path.join(cc.test_dir, "temp_smag") return def tearDown(self): @@ -182,15 +173,15 @@ def test_supermag2ascii_ocb_bad_ocb(self): return -class TestSuperMAGLoadMethods(unittest.TestCase): +class TestSuperMAGLoadMethods(cc.TestLogWarnings): """Test the SuperMAG loading functions.""" def setUp(self): """Initialize the test set up.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_file = os.path.join(self.ocb_dir, "tests", "test_data", - "test_smag") + super().setUp() + + self.test_file = os.path.join(cc.test_dir, "test_smag") self.test_vals = {'BE': -6.0, 'BN': -23.6, 'BZ': -25.2, 'DAY': 5, 'DEC': 17.13, 'HOUR': 13, 'MIN': 40, 'MLAT': 77.22, 'DATETIME': dt.datetime(2000, 5, 5, 13, 40, 30), @@ -198,12 +189,14 @@ def setUp(self): 'SML': -195, 'SMU': 124, 'STID': "THL", 'SZA': 76.97, 'YEAR': 2000} self.out = list() - self.assertTrue(os.path.isfile(self.test_file)) + self.assertTrue(os.path.isfile(self.test_file), + msg="'{:}' is not a file".format(self.test_file)) return def tearDown(self): """Tear down the test environment.""" del self.test_file, self.out, self.test_vals + super().tearDown() return def test_load_supermag_ascii_data(self): @@ -225,11 +218,15 @@ def test_load_supermag_ascii_data(self): def test_load_failures(self): """Test graceful failures with different bad file inputs.""" - for val in ['fake_file', os.path.join(self.ocb_dir, "test", - "test_data", "test_vort")]: + for val, self.lwarn in [('fake_file', 'name provided is not a file'), + (os.path.join(cc.test_dir, "test_vort"), + 'no data in the SuperMAG file')]: with self.subTest(val=val): self.out = ocb_ismag.load_supermag_ascii_data(val) - self.assertListEqual(self.out[0], list()) + # Test the logging message + self.eval_logging_message() + + # Test the output, header may or may not be empty self.assertDictEqual(self.out[1], dict()) return From 30dfeeeb561418a25ed6b1d1f538f9a35ff15f91 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 26 Mar 2024 17:42:51 -0400 Subject: [PATCH 14/73] MAINT: addressed deprecation warning Addressed a deprecation warning in the evaluation. --- ocbpy/ocb_scaling.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ocbpy/ocb_scaling.py b/ocbpy/ocb_scaling.py index ecc33c2d..cf7c3538 100644 --- a/ocbpy/ocb_scaling.py +++ b/ocbpy/ocb_scaling.py @@ -337,12 +337,16 @@ def _test_update_bound_shape(self): """ # Test the OCB input shape - oshapes = np.unique([self.ocb_lat.shape, self.ocb_mlt.shape, - self.r_corr.shape]) - oshape = () if len(oshapes) == 0 else oshapes.max() + oshapes = list() + for oshape in [self.ocb_lat.shape, self.ocb_mlt.shape, + self.r_corr.shape]: + if oshape not in oshapes: + oshapes.append(oshape) - if(oshape != self.ocb_ind.shape or len(oshapes) > 2 - or (len(oshapes) == 2 and min(oshapes) != ())): + oshape = () if len(oshapes) == 0 else max(oshapes) + + if(np.array(oshape).size != self.ocb_ind.size or len(oshapes) > 2 + or (len(oshapes) == 2 and len(min(oshapes)) > 0)): raise ValueError('OCB index and input shapes mismatched') # Compare and update the vector data shape if needed From 6733f8a676114073720ceda0984da206febb844e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 26 Mar 2024 17:45:57 -0400 Subject: [PATCH 15/73] STY: reduced duplicate code in pysat_instruments Reduced duplicate code and added checks by: - ensuring `max_sdiff` is always an integer, - specifying keys instead of accessing them impicitly, - using descriptive variables in evaluations, - reducing the number of loops by removing duplicate times from time index cycle, - added a function to pad and flatten data, and - combined similar processing loops. --- ocbpy/instruments/pysat_instruments.py | 121 ++++++++++++++++--------- 1 file changed, 78 insertions(+), 43 deletions(-) diff --git a/ocbpy/instruments/pysat_instruments.py b/ocbpy/instruments/pysat_instruments.py index db8cd91c..d2b10a74 100644 --- a/ocbpy/instruments/pysat_instruments.py +++ b/ocbpy/instruments/pysat_instruments.py @@ -102,7 +102,6 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, 'scale_func': local_scale_func}} """ - # Test the input if evar_names is None: evar_names = [] @@ -134,6 +133,9 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, for eattr in curl_evar_names]): raise ValueError('at least one unknown E field name') + # Ensure the correct data format + max_sdiff = int(max_sdiff) + # Format the new data column names olat_name = "{:s}_ocb".format(mlat_name) omlt_name = "{:s}_ocb".format(mlt_name) @@ -250,7 +252,7 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, >= hemisphere) finite_mask = np.isfinite(nan_inst[pysat_names].to_array().max( 'variable')) - dat_coords = [coord for coord in pysat_inst[pysat_names].coords] + dat_coords = [coord for coord in pysat_inst[pysat_names].coords.keys()] dat_ind = np.where(finite_mask) # Test the OCB data @@ -289,8 +291,8 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, else: ocb_coords = [pysat_inst.index.name] - # See if the data index has more dimensions than the coordinates - if len(dat_ind) > len(ocb_coords): + # See if the data has more dimensions than the OCB coordinates + if len(dat_coords) > len(ocb_coords): combo_shape = list(aacgm_lat.shape) for dcoord in dat_coords: if dcoord not in ocb_coords: @@ -319,25 +321,23 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, # Cycle through the data, matching data and OCB records idat = 0 - ndat = len(dat_ind[0]) + uind = np.unique(dat_ind[0]) + ndat = len(uind) if hasattr(ocb, "boundary_lat"): ref_r = 90.0 - abs(ocb.boundary_lat) else: ref_r = 90.0 - abs(ocb.ocb.boundary_lat) while idat < ndat and ocb.rec_ind < ocb.records: - idat = ocbpy.match_data_ocb(ocb, pysat_inst.index[dat_ind[0]], + idat = ocbpy.match_data_ocb(ocb, pysat_inst.index[uind], idat=idat, max_tol=max_sdiff, min_merit=min_merit, max_merit=max_merit, **kwargs) if idat < ndat and ocb.rec_ind < ocb.records: - # Find all the indices with the same time - time_ind = np.where(pysat_inst.index[dat_ind[0]] - == pysat_inst.index[dat_ind[0]][idat]) - idat = time_ind[0][-1] + time_ind = np.where(dat_ind[0] == uind[idat])[0] - if len(dat_ind) > 1: + if len(dat_coords) > 1: iout = tuple(dind[time_ind] for dind in dat_ind) if pysat_var_names > 1: # If there is more than one variable, need to downselect @@ -348,7 +348,7 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, time_mask = np.isfinite(time_sel.where( finite_mask & (pysat_inst[pysat_inst.index.name] - == pysat_inst.index[dat_ind[0]][idat]))) + == pysat_inst.index[uind][idat]))) vind = iout[0] else: iout = dat_ind[0][time_ind] @@ -378,6 +378,7 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, "dat_name": None, "dat_units": None, "scale_func": None} vector_init = dict(vector_default) + vshape = list() for eattr in vector_names.keys(): oattr = "{:s}_ocb".format(eattr) @@ -385,22 +386,23 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, # Not all vector names are DataFrame names vname = vector_names[eattr][ikey] if vname in pysat_inst.variables: - # Test to see if the input is appropriately shaped - if(not pysat_inst.pandas_format - and len(ocb_coords) > len(pysat_inst[ - vname].coords)): - raise ValueError(''.join([ - 'vector variables must all have the same', - ' dimensions'])) - if time_mask is None: vector_init[ikey] = pysat_inst[vname][iout] else: - vector_init[ikey] = pysat_inst[vname].where( - time_mask, drop=True).values.flatten() + vector_init[ikey] = reshape_pad_mask_flatten( + pysat_inst[vname], time_mask) + + # Save the vector shapes for testing + if vector_init[ikey].shape not in vshape: + vshape.append(vector_init[ikey].shape) else: vector_init[ikey] = vname + # Evaluate the consistency of the vector inputs + if len(vshape) > 1: + raise ValueError( + 'vector variables must all have the same shape') + # Perform the vector scaling vout = ocbscal.VectorData(vind, ocb.rec_ind, aacgm_lat[iout], @@ -421,27 +423,21 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, unscaled_r = ocb.r[ocb.rec_ind] + ocb_output[ ocor_name][iout] - # Scale the E-field proportional variables - for eattr in evar_names: - oattr = "{:s}_ocb".format(eattr) - if time_mask is None: - evar = pysat_inst[eattr][iout] - else: - evar = pysat_inst[eattr].where(time_mask, - drop=True).values.flatten() - ocb_output[oattr][iout] = ocbscal.normal_evar( - evar, unscaled_r, ref_r) - - # Scale the variables proportial to the curl of the E-field - for eattr in curl_evar_names: - oattr = "{:s}_ocb".format(eattr) - if time_mask is None: - evar = pysat_inst[eattr][iout] - else: - evar = pysat_inst[eattr].where(time_mask, - drop=True).values.flatten() - ocb_output[oattr][iout] = ocbscal.normal_curl_evar( - evar, unscaled_r, ref_r) + # Scale the proportional variables + for scale_names, scale_func in [ + (evar_names, ocbscal.normal_evar), + (curl_evar_names, ocbscal.normal_curl_evar)]: + for eattr in scale_names: + oattr = "{:s}_ocb".format(eattr) + if time_mask is None: + evar = pysat_inst[eattr][iout] + else: + evar = reshape_pad_mask_flatten(pysat_inst[eattr], + time_mask) + + # Scale the variable + ocb_output[oattr][iout] = scale_func(evar, unscaled_r, + ref_r) # Move to next line idat += 1 @@ -489,6 +485,45 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, return +def reshape_pad_mask_flatten(data, mask): + """Reshape, pad, mask, and flatten data. + + Parameters + ---------- + data : xr.DataArray + Data to be reshaped, padded, masked, and flattened for processing. + mask : xr.DataArray + Mask with the desired dimensions + + Returns + ------- + flat : np.array + Flattened array of good data, as specified by the mask + + """ + if np.all(mask.dims == data.dims): + flat = data.where(mask, drop=True).values.flatten() + else: + # Reshape this data variable for existing dims + data_dims = [dim for dim in mask.dims if dim in data.dims] + flat = data.transpose(*data_dims).values + + # Pad by adding the additional dimensions if needed + if len(data_dims) < len(mask.dims): + try: + flat = np.full(shape=tuple(reversed(list(mask.shape))), + fill_value=flat.transpose()).transpose() + except Exception as aerr: + # Using Exception instead of AssertionError because the + # catch is not consistent + raise ValueError(''.join(['vector variables must all have the', + ' same shape, {:}'.format(aerr)])) + + # Downselect and flatten the data + flat = flat[mask.values].flatten() + + return flat + def add_ocb_to_metadata(pysat_inst, ocb_name, pysat_name, overwrite=False, notes='', isvector=False): From 59efe3d76a1e4d0c7ea92bab619d54c5da499e91 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 26 Mar 2024 17:47:03 -0400 Subject: [PATCH 16/73] TST: updated pysat unit tests Updated pysat unit tests by: - using common testing classes and variables, - updating instruments and load flags for upcoming pysat release, and - combined similar unit tests using subTest. --- ocbpy/tests/test_pysat.py | 1146 ++++++++++++------------------------- 1 file changed, 371 insertions(+), 775 deletions(-) diff --git a/ocbpy/tests/test_pysat.py b/ocbpy/tests/test_pysat.py index 314d683d..d2859aa5 100644 --- a/ocbpy/tests/test_pysat.py +++ b/ocbpy/tests/test_pysat.py @@ -5,14 +5,13 @@ # ----------------------------------------------------------------------------- """Tests the ocbpy.instruments.pysat functions.""" -from io import StringIO -import logging import numpy as np from os import path from packaging import version import unittest import ocbpy +import ocbpy.tests.class_common as cc try: # Import pysat first to get the correct error message @@ -160,7 +159,8 @@ def test_ocb_added(self): match_data = self.test_inst[self.ocb_key] mask_data = np.isfinite(match_data) - self.assertTrue(np.array(mask_data).any()) + self.assertTrue(np.array(mask_data).any(), + msg="No OCB data for {:}".format(self.ocb_key)) mind = np.where(np.array(mask_data)) match_data = np.array(match_data)[mind] @@ -171,7 +171,8 @@ def test_ocb_added(self): - self.ocb.dtime).min().total_seconds() self.assertLessEqual(check_time, self.del_time, msg="".join(["bad time difference for ", - "OCB key ", self.ocb_key])) + "OCB key ", self.ocb_key, + " at {:}".format(ii)])) if(self.pysat_key is not None and self.pysat_key not in [self.pysat_lat, 'mlt']): @@ -234,64 +235,79 @@ def test_add_ocb_to_metadata_defaults(self): @unittest.skipIf(no_pysat, "pysat not installed") -class TestPysatMethods(TestPysatUtils): +class TestPysatMethods(TestPysatUtils, cc.TestLogWarnings): """Integration tests for using ocbpy on pysat pandas data.""" def setUp(self): """Initialize the test class.""" # Set the util default values - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 + TestPysatUtils.setUp(self) + + # Set the logging values + cc.TestLogWarnings.setUp(self) # Set the method default values - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb_kw = {"ocbfile": self.test_file, + self.test_file = path.join(cc.test_dir, "test_north_circle") + self.ocb_kw = {"filename": self.test_file, "instrument": "image", "hemisphere": 1} - self.ocb = ocbpy.OCBoundary(self.test_file, instrument="image", - hemisphere=1) - self.ocb.rec_ind = 27 - + self.ocb_class = ocbpy.OCBoundary + self.rec_ind = 27 self.pysat_var2 = 'dummy2' - self.test_inst = pysat.Instrument('pysat', 'testing', num_samples=50400, - clean_level='clean', - update_files=True, + self.test_module = pysat.instruments.pysat_testing + + return + + def tearDown(self): + """Tear down after each test.""" + + cc.TestLogWarnings.tearDown(self) + TestPysatUtils.tearDown(self) + + del self.test_file, self.ocb_kw, self.ocb_class, self.rec_ind + del self.pysat_var2, self.test_module + return + + def load_boundaries(self): + """Load the OCB boundary class object for testing.""" + # Verify the existence of the test file + self.assertTrue(path.isfile(self.test_file), + msg="'{:}' is not a file".format(self.test_file)) + + # Set up the OCB object + self.ocb = self.ocb_class(**self.ocb_kw) + if self.rec_ind < self.ocb.records: + self.ocb.rec_ind = self.rec_ind + return + + def load_instrument(self): + """Load the pysat Instrument for testing.""" + if self.ocb is None: + self.load_boundaries() + + self.test_inst = pysat.Instrument(inst_module=self.test_module, + num_samples=50400, file_date_range=pds.date_range( self.ocb.dtime[0], self.ocb.dtime[-1], freq='1D')) + # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): + # TODO(#130) remove version checking by updating minimum supported pysat + load_kwargs = {'date': self.ocb.dtime[self.rec_ind]} + if version.Version(pysat.__version__) > version.Version( + '3.0.1') and version.Version( + pysat.__version__) < version.Version('3.2.0'): load_kwargs['use_header'] = True self.test_inst.load(**load_kwargs) - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - - return - - def tearDown(self): - """Tear down after each test.""" - - del self.ocb_key, self.pysat_key, self.notes - del self.test_inst, self.ocb, self.added_keys, self.pysat_keys - del self.test_file, self.log_capture, self.pysat_lat - del self.lout, self.lwarn, self.ocb_kw, self.pysat_var2, self.del_time return def test_add_ocb_to_metadata(self): """Test the metadata adding routine.""" + # Load the data and boundaries + self.load_instrument() + + # Test the metadata operation ocb_pysat.add_ocb_to_metadata(self.test_inst, "ocb_test", self.pysat_key, notes="test notes") @@ -300,6 +316,10 @@ def test_add_ocb_to_metadata(self): def test_add_ocb_to_metadata_vector(self): """Test the metadata adding routine for vector data.""" + # Load the data and boundaries + self.load_instrument() + + # Test the metadata operation ocb_pysat.add_ocb_to_metadata(self.test_inst, "ocb_test", self.pysat_key, notes="test notes scaled using None", @@ -310,18 +330,25 @@ def test_add_ocb_to_metadata_vector(self): def test_no_overwrite_metadata(self): """Test the overwrite block on metadata adding routine.""" + # Load the data and boundaries + self.load_instrument() + + # Test the metadata overwriting failure self.test_add_ocb_to_metadata() ocb_pysat.add_ocb_to_metadata(self.test_inst, "ocb_test", self.pysat_key, notes="test notes two", overwrite=False) self.lwarn = u"OCB data already has metadata" - self.lout = self.log_capture.getvalue() - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() return def test_overwrite_metadata(self): """Test the overwrite permission on metadata adding routine.""" + # Load the data and boundaries + self.load_instrument() + + # Test the metadata overwriting success self.test_add_ocb_to_metadata() ocb_pysat.add_ocb_to_metadata(self.test_inst, "ocb_test", self.pysat_key, notes="test notes two", @@ -333,6 +360,10 @@ def test_overwrite_metadata(self): def test_add_ocb_to_data_ocb_obj(self): """Test adding ocb to pysat data using the loaded OCB object.""" + # Load the data and boundaries + self.load_instrument() + + # Test adding OCBs ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", ocb=self.ocb, max_sdiff=self.del_time) @@ -346,6 +377,17 @@ def test_add_ocb_to_data_ocb_obj(self): def test_add_ocb_to_data_ocb_file(self): """Test adding ocb to pysat data using the OCB file name.""" + # Load the data and boundaries + self.load_instrument() + + # This test will not work for Dual-Boundary objects + if isinstance(self.ocb, ocbpy._boundary.DualBoundary): + self.skipTest("".join(["`pysat_instruments.add_ocb_to_data` ", + "requires object initialization for ", + "DualBoundary calculations."])) + + # Test adding OCBs using filename instead of OCB object + self.ocb_kw['ocbfile'] = self.ocb_kw['filename'] ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", max_sdiff=self.del_time, **self.ocb_kw) @@ -359,6 +401,8 @@ def test_add_ocb_to_data_ocb_file(self): def test_add_ocb_to_data_southern_hemisphere(self): """Test successful identification of southern hemisphere only.""" + # Load the data and boundaries + self.load_instrument() # Don't set the hemisphere del self.ocb_kw['hemisphere'] @@ -387,6 +431,10 @@ def test_add_ocb_to_data_southern_hemisphere(self): def test_add_ocb_to_data_evar(self): """Test adding ocb to pysat with E-field related variables.""" + # Load the data and boundaries + self.load_instrument() + + # Add the OCB with electrically scaled variables ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", evar_names=[self.pysat_key], ocb=self.ocb, max_sdiff=self.del_time) @@ -402,6 +450,10 @@ def test_add_ocb_to_data_evar(self): def test_add_ocb_to_data_curl_evar(self): """Test adding ocb to pysat with Curl E-field related variables.""" + # Load the data and boundaries + self.load_instrument() + + # Add the OCB with curl-scaled variables ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", curl_evar_names=[self.pysat_var2], ocb=self.ocb, max_sdiff=self.del_time) @@ -417,6 +469,10 @@ def test_add_ocb_to_data_curl_evar(self): def test_add_ocb_to_data_evar_vect(self): """Test adding ocb to pysat with Curl E-field related vectors.""" + # Load the data and boundaries + self.load_instrument() + + # Add the OCB with electrically scaled vectors ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", evar_names=['vect_evar'], vector_names={ @@ -443,6 +499,10 @@ def test_add_ocb_to_data_evar_vect(self): def test_add_ocb_to_data_curl_evar_vect(self): """Test adding ocb to pysat with Curl E-field related vectors.""" + # Load the data and boundaries + self.load_instrument() + + # Add the OCB with curl-scaled vectors ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", curl_evar_names=['vect_cevar'], vector_names={ @@ -469,7 +529,10 @@ def test_add_ocb_to_data_curl_evar_vect(self): def test_add_ocb_to_data_custom_vect(self): """Test adding ocb to pysat with custom scaled variables.""" + # Load the data and boundaries + self.load_instrument() + # Add the OCB with a custom scaling function ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", vector_names={ 'vect_cust': @@ -495,6 +558,10 @@ def test_add_ocb_to_data_custom_vect(self): def test_add_ocb_to_data_all_types(self): """Test adding ocb to pysat with E-field, Curl, and Vector data.""" + # Load the data and boundaries + self.load_instrument() + + # Add the OCB with multiple inputs ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", evar_names=[self.pysat_key], curl_evar_names=[self.pysat_var2], @@ -523,17 +590,23 @@ def test_add_ocb_to_data_all_types(self): def test_add_ocb_to_data_no_file(self): """Test adding ocb to pydat when no OCB file or data is provided.""" + # Load the data and boundaries + self.load_instrument() + + # Add the OCB without a filename self.ocb_kw['ocbfile'] = None ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", **self.ocb_kw, max_sdiff=self.del_time) self.lwarn = u"no data in Boundary file(s)" - self.lout = self.log_capture.getvalue() - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() return def test_add_ocb_to_data_bad_hemisphere_selfset(self): """Test failure of a pysat.Instrument to specify a hemisphere.""" + # Load the data and boundaries + self.load_instrument() + del self.ocb_kw['hemisphere'] with self.assertRaisesRegex( @@ -544,6 +617,8 @@ def test_add_ocb_to_data_bad_hemisphere_selfset(self): def test_bad_pysat_inst(self): """Test failure of a bad pysat.Instrument in pysat functions.""" + # Load the data and boundaries + self.load_instrument() # Set the function and input data func_dict = {ocb_pysat.add_ocb_to_data: [None, self.pysat_lat, "mlt"], @@ -560,6 +635,8 @@ def test_bad_pysat_inst(self): def test_add_ocb_to_data_bad_mlat(self): """Test failure of unknown mlat key in add_ocb_to_data.""" + # Load the data and boundaries + self.load_instrument() with self.assertRaisesRegex(ValueError, 'unknown magnetic latitude name bad'): @@ -568,6 +645,9 @@ def test_add_ocb_to_data_bad_mlat(self): def test_add_ocb_to_data_bad_mlt(self): """Test failure of unknown mlt key in add_ocb_to_data.""" + # Load the data and boundaries + self.load_instrument() + with self.assertRaisesRegex(ValueError, 'unknown magnetic local time name bad'): ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "bad", @@ -576,6 +656,9 @@ def test_add_ocb_to_data_bad_mlt(self): def test_add_ocb_to_data_bad_evar(self): """Test failure of unknown E field key in add_ocb_to_data.""" + # Load the data and boundaries + self.load_instrument() + with self.assertRaisesRegex(ValueError, 'at least one unknown E field name'): ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", @@ -584,6 +667,9 @@ def test_add_ocb_to_data_bad_evar(self): def test_add_ocb_to_data_bad_curl(self): """Test failure of unknown E field key in add_ocb_to_data.""" + # Load the data and boundaries + self.load_instrument() + with self.assertRaisesRegex(ValueError, 'at least one unknown E field name'): ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", @@ -592,6 +678,9 @@ def test_add_ocb_to_data_bad_curl(self): def test_add_ocb_to_data_bad_vector_scale(self): """Test failure of missing scaling function in add_ocb_to_data.""" + # Load the data and boundaries + self.load_instrument() + with self.assertRaisesRegex(ValueError, 'missing scaling function for bad'): ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", @@ -604,6 +693,9 @@ def test_add_ocb_to_data_bad_vector_scale(self): def test_add_ocb_to_data_bad_vector_name(self): """Test failure of missing scaling function in add_ocb_to_data.""" + # Load the data and boundaries + self.load_instrument() + with self.assertRaisesRegex(ValueError, 'unknown vector name bad_n'): ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", @@ -624,56 +716,17 @@ class TestPysatMethodsEAB(TestPysatMethods): def setUp(self): """Initialize the test class.""" + # Initalize the defaults + super().setUp() - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 - - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_eab") - self.assertTrue(path.isfile(self.test_file)) - self.ocb_kw = {"ocbfile": self.test_file, + # Update the class defaults + self.test_file = path.join(cc.test_dir, "test_north_eab") + self.ocb_kw = {"filename": self.test_file, "instrument": "image", "hemisphere": 1} - self.ocb = ocbpy.EABoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.pysat_var2 = 'dummy2' - self.test_inst = pysat.Instrument('pysat', 'testing', num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): - load_kwargs['use_header'] = True - self.test_inst.load(**load_kwargs) - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) + self.ocb_class = ocbpy.EABoundary return - def tearDown(self): - """Tear down after each test.""" - - del self.test_file, self.log_capture, self.ocb, self.test_inst - del self.lout, self.lwarn, self.ocb_kw, self.pysat_var2, self.ocb_key - del self.pysat_key, self.notes, self.pysat_lat, self.del_time - del self.added_keys, self.pysat_keys - return - @unittest.skipIf(no_pysat, "pysat not installed, cannot test routines") class TestPysatMethodsDual(TestPysatMethods): @@ -681,175 +734,37 @@ class TestPysatMethodsDual(TestPysatMethods): def setUp(self): """Initialize the test class.""" + # Initalize the defaults + super().setUp() - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 - - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb_kw = {"ocbfile": self.test_file, - "instrument": "image", "hemisphere": 1} - self.ocb = ocbpy.DualBoundary(ocb_filename=self.test_file, - ocb_instrument='image', - eab_filename=self.test_file.replace( - "north_circle", "north_eab"), - eab_instrument='image', - hemisphere=1) - self.ocb.rec_ind = 0 - - self.pysat_var2 = 'dummy2' - self.test_inst = pysat.Instrument('pysat', 'testing', num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): - load_kwargs['use_header'] = True - self.test_inst.load(**load_kwargs) - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) + # Update the method defaults + self.ocb_class = ocbpy.DualBoundary + self.ocb_kw = {'ocb_filename': self.test_file, + 'ocb_instrument': 'image', + 'eab_filename': self.test_file.replace('north_circle', + 'north_eab'), + 'eab_instrument': 'image', 'hemisphere': 1} + self.rec_ind = 0 return - def tearDown(self): - """Tear down after each test.""" - - del self.test_file, self.log_capture, self.ocb, self.test_inst - del self.lout, self.lwarn, self.ocb_kw, self.pysat_var2, self.pysat_lat - del self.ocb_key, self.pysat_key, self.notes, self.del_time - del self.added_keys, self.pysat_keys - return - - -@unittest.skipIf(no_pysat, "pysat not installed") -class TestPysatMethods2DXarray(TestPysatMethods): - """Integration tests for using ocbpy on pysat 2D Xarray data.""" - - def setUp(self): - """Initialize the test class.""" - - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 - - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb_kw = {"ocbfile": self.test_file, - "instrument": "image", "hemisphere": 1} - self.ocb = ocbpy.OCBoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.pysat_var2 = 'dummy2' - self.test_inst = pysat.Instrument('pysat', 'testing2d_xarray', - num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - - # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): - load_kwargs['use_header'] = True - self.test_inst.load(**load_kwargs) - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - - return - - def tearDown(self): - """Tear down after each test.""" - - del self.test_file, self.log_capture, self.ocb, self.test_inst - del self.lout, self.lwarn, self.ocb_kw, self.pysat_var2, self.pysat_lat - del self.ocb_key, self.pysat_key, self.notes, self.del_time - del self.added_keys, self.pysat_keys - return - @unittest.skipIf(no_pysat, "pysat not installed") class TestPysatMethodsXarray(TestPysatMethods): - """Integration tests for using ocbpy on pysat Xarray data.""" + """Integration tests for using ocbpy on pysat ND or 2D Xarray data.""" def setUp(self): """Initialize the test class.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 + # Initalize the defaults + super().setUp() - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb_kw = {"ocbfile": self.test_file, - "instrument": "image", "hemisphere": 1} - self.ocb = ocbpy.OCBoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.pysat_var2 = 'dummy2' - self.test_inst = pysat.Instrument('pysat', 'testing_xarray', - num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - - # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): - load_kwargs['use_header'] = True - self.test_inst.load(**load_kwargs) - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - - return - - def tearDown(self): - """Clean the test environment.""" + # Update the method defaults + # TODO(#130) remove version checking by updating minimum supported pysat + if version.Version(pysat.__version__) < version.Version('3.1.0'): + self.test_module = pysat.instruments.pysat_testing2d_xarray + else: + self.test_module = pysat.instruments.pysat_ndtesting - del self.test_file, self.log_capture, self.ocb, self.test_inst - del self.lout, self.lwarn, self.ocb_kw, self.pysat_var2, self.del_time - del self.ocb_key, self.pysat_key, self.notes, self.pysat_lat - del self.added_keys, self.pysat_keys return @@ -859,58 +774,18 @@ class TestPysatMethodsModel(TestPysatMethods): def setUp(self): """Initialize the test class.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy2' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 + # Initalize the defaults + super().setUp() - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb_kw = {"ocbfile": self.test_file, - "instrument": "image", "hemisphere": 1} - self.ocb = ocbpy.OCBoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.pysat_var2 = 'dummy2' - self.test_inst = pysat.Instrument('pysat', 'testmodel', - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - - # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): - load_kwargs['use_header'] = True - self.test_inst.load(**load_kwargs) - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - - return - - def tearDown(self): - """Clean the test environment.""" - - del self.test_file, self.log_capture, self.ocb, self.test_inst - del self.lout, self.lwarn, self.ocb_kw, self.pysat_var2, self.del_time - del self.ocb_key, self.pysat_key, self.notes, self.pysat_lat - del self.added_keys, self.pysat_keys + # Update the method default + self.test_module = pysat.instruments.pysat_testmodel return def test_mismatched_vector_data(self): """Test that vector data with different dimensions fails.""" + # Load the data and boundaries + self.load_instrument() + with self.assertRaisesRegex(ValueError, 'vector variables must all have the same'): ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", @@ -926,303 +801,223 @@ def test_mismatched_vector_data(self): @unittest.skipIf(no_pysat, "pysat not installed") -class TestPysatCustMethods(TestPysatUtils): +class TestPysatCustMethods(TestPysatUtils, cc.TestLogWarnings): """Integration tests for using ocbpy as a custom function with pysat pandas. """ def setUp(self): """Initialize the unit tests for using the pysat.Custom methods.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 + + # Set the util default values + TestPysatUtils.setUp(self) + + # Set the logging values + cc.TestLogWarnings.setUp(self) # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb = ocbpy.OCBoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.test_inst = pysat.Instrument('pysat', 'testing', num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) + self.test_file = path.join(cc.test_dir, "test_north_circle") + self.ocb_kw = {"filename": self.test_file, "instrument": "image", + "hemisphere": 1} + self.ocb_class = ocbpy.OCBoundary + self.rec_ind = 27 + self.test_module = pysat.instruments.pysat_testing self.pysat_var2 = 'dummy2' self.cust_kwargs = {'mlat_name': self.pysat_lat, 'mlt_name': 'mlt', - 'ocb': self.ocb, 'max_sdiff': self.del_time} - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) + 'max_sdiff': self.del_time} + self.pysat_kw = {} return def tearDown(self): """Clean the test environment.""" - del self.ocb_key, self.pysat_key, self.notes, self.pysat_lat - del self.added_keys, self.pysat_keys, self.test_inst, self.ocb - del self.test_file, self.log_capture, self.cust_kwargs, self.pysat_var2 - del self.lwarn, self.lout, self.del_time - return - - def test_load(self): - """Test the pysat file loading.""" - # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): - load_kwargs['use_header'] = True - - self.test_inst.load(**load_kwargs) - self.assertFalse(self.test_inst.empty) - self.assertIn(self.cust_kwargs['mlat_name'], self.test_inst.variables) - self.assertIn('mlt', self.test_inst.variables) - return - - def test_cust_add_ocb_to_data_ocb_obj(self): - """Test adding ocb to pysat data using the loaded OCB object.""" - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - self.test_load() - - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = [aa.split("_ocb")[0] for aa in self.added_keys] - self.assertIn('r_corr', self.pysat_keys) - self.pysat_keys[self.pysat_keys.index("r_corr")] = None - self.test_ocb_added() - return - - def test_cust_add_ocb_to_data_ocb_file(self): - """Test adding ocb to pysat data with load using the OCB file name.""" - del self.cust_kwargs['ocb'] - self.cust_kwargs['ocbfile'] = self.test_file - self.cust_kwargs['instrument'] = 'image' - self.cust_kwargs['hemisphere'] = 1 - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - self.test_load() - - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = [aa.split("_ocb")[0] for aa in self.added_keys] - self.assertIn('r_corr', self.pysat_keys) - self.pysat_keys[self.pysat_keys.index("r_corr")] = None - self.test_ocb_added() - return + cc.TestLogWarnings.tearDown(self) + TestPysatUtils.tearDown(self) - def test_cust_add_ocb_to_data_evar(self): - """Test adding ocb to pysat with load including E-field variables.""" - self.cust_kwargs['evar_names'] = [self.pysat_key] - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - self.test_load() - - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = [aa.split("_ocb")[0] for aa in self.added_keys] - self.assertIn('r_corr', self.pysat_keys) - self.pysat_keys[self.pysat_keys.index("r_corr")] = None - self.test_ocb_added() + del self.test_file, self.ocb_kw, self.ocb_class, self.pysat_kw + del self.pysat_var2, self.test_module, self.cust_kwargs return - def test_cust_add_ocb_to_data_curl_evar(self): - """Test adding ocb to pysat including Curl E-field variables.""" - - self.cust_kwargs['curl_evar_names'] = [self.pysat_var2] - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - self.test_load() - - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = [aa.split("_ocb")[0] for aa in self.added_keys] - self.assertIn('r_corr', self.pysat_keys) - self.pysat_keys[self.pysat_keys.index("r_corr")] = None + def load_boundaries(self): + """Load the OCB boundary class object for testing.""" + # Verify the existence of the test file + self.assertTrue(path.isfile(self.test_file), + msg="'{:}' is not a file".format(self.test_file)) - self.test_ocb_added() + # Set up the OCB object + self.ocb = self.ocb_class(**self.ocb_kw) + if self.rec_ind < self.ocb.records: + self.ocb.rec_ind = self.rec_ind return - def test_cust_add_ocb_to_data_evar_vect(self): - """Test adding ocb to pysat with load including Curl E-field vectors.""" - - self.cust_kwargs['evar_names'] = ['vect_evar'] - self.cust_kwargs['vector_names'] = {'vect_evar': - {'aacgm_n': self.pysat_key, - 'aacgm_e': self.pysat_var2, - 'dat_name': 'vect', - 'dat_units': 'm/s'}} + def load_instrument(self): + """Load the pysat Instrument for testing.""" + if self.ocb is None: + self.load_boundaries() - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) + self.pysat_kw['file_date_range'] = pds.date_range( + self.ocb.dtime[0], self.ocb.dtime[-1], freq='1D') - self.test_load() + self.test_inst = pysat.Instrument(inst_module=self.test_module, + num_samples=50400, **self.pysat_kw) - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = list() - for aa in self.added_keys: - pp = aa.split("_ocb")[0] - if pp == "r_corr": - pp = None - elif pp not in self.test_inst.variables: - pp = self.pysat_key - self.pysat_keys.append(pp) - self.test_ocb_added() - return - - def test_cust_add_ocb_to_data_curl_evar_vect(self): - """Test adding ocb to pysat including Curl E-field vectors.""" - self.cust_kwargs['curl_evar_names'] = ['vect_cevar'] - self.cust_kwargs['vector_names'] = {'vect_cevar': - {'aacgm_n': self.pysat_key, - 'aacgm_e': self.pysat_var2, - 'dat_name': 'vect', - 'dat_units': 'm/s'}} - - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - self.test_load() + # Reduce pysat warnings + # TODO(#130) remove version checking by updating minimum supported pysat + load_kwargs = {'date': self.ocb.dtime[self.rec_ind]} + if version.Version(pysat.__version__) > version.Version( + '3.0.1') and version.Version( + pysat.__version__) < version.Version('3.2.0'): + load_kwargs['use_header'] = True - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = list() - for aa in self.added_keys: - pp = aa.split("_ocb")[0] - if pp == "r_corr": - pp = None - elif pp not in self.test_inst.variables: - pp = self.pysat_key - self.pysat_keys.append(pp) - self.test_ocb_added() + self.test_inst.load(**load_kwargs) return - def test_cust_add_ocb_to_data_custom_vect(self): - """Test adding ocb to pysat including custom scaled variables.""" - self.cust_kwargs['vector_names'] = {'vect_cust': - {'aacgm_n': self.pysat_key, - 'aacgm_e': self.pysat_var2, - 'dat_name': 'vect', - 'dat_units': 'm/s', - 'scale_func': None}} - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - self.test_load() - - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = list() - for aa in self.added_keys: - pp = aa.split("_ocb")[0] - if pp == "r_corr": - pp = None - elif pp not in self.test_inst.variables: - pp = self.pysat_key - self.pysat_keys.append(pp) + def test_load(self): + """Test the pysat file loading without custom functions.""" + # Load the data and boundaries + self.load_instrument() - self.test_ocb_added() + # Test the load for expected variables (with or without a custom func) + self.assertFalse(self.test_inst.empty, + msg="No data loaded for {:}".format(self.test_inst)) + self.assertIn(self.cust_kwargs['mlat_name'], self.test_inst.variables) + self.assertIn('mlt', self.test_inst.variables) return - def test_cust_add_ocb_to_data_all_types(self): - """Test adding ocb to pysat including E-field, Curl, & vects.""" - self.cust_kwargs['evar_names'] = [self.pysat_key] - self.cust_kwargs['curl_evar_names'] = [self.pysat_var2] - self.cust_kwargs['vector_names'] = {'vect_cust': - {'aacgm_n': self.pysat_key, - 'aacgm_e': self.pysat_var2, - 'dat_name': 'vect', - 'dat_units': 'm/s', - 'scale_func': None}} - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - self.test_load() - - self.added_keys = [kk for kk in self.test_inst.meta.keys() - if kk.find('_ocb') > 0] - self.pysat_keys = list() - for aa in self.added_keys: - pp = aa.split("_ocb")[0] - if pp == "r_corr": - pp = None - elif pp not in self.test_inst.variables: - pp = self.pysat_key - self.pysat_keys.append(pp) - - self.test_ocb_added() + def test_cust_add_ocb_to_data(self): + """Test adding ocb to pysat data using the loaded OCB object.""" + # Load the boundaries + self.load_boundaries() + + # Cycle through the different custom inputs + for kw, val in [(['ocb'], [self.ocb]), + (['ocbfile', 'instrument', 'hemisphere'], + [self.test_file, 'image', 1]), + (['ocb', 'evar_names'], [self.ocb, [self.pysat_key]]), + (['ocb', 'curl_evar_names'], + [self.ocb, [self.pysat_var2]])]: + # Ensure the record index is correct + self.ocb.rec_ind = self.rec_ind + + # Update the Instrument to include a custom function + self.pysat_kw['custom'] = [{'function': ocb_pysat.add_ocb_to_data, + 'kwargs': dict(self.cust_kwargs)}] + + # Update the custom function kwargs + for i, kw_val in enumerate(kw): + self.pysat_kw['custom'][0]['kwargs'][kw_val] = val[i] + + with self.subTest(cust_kwargs=self.pysat_kw['custom'][0]['kwargs']): + # Load and test the defaults + self.test_load() + + # Test the additional outputs + self.added_keys = [kk for kk in self.test_inst.meta.keys() + if kk.find('_ocb') > 0] + self.pysat_keys = [aa.split("_ocb")[0] + for aa in self.added_keys] + self.assertIn('r_corr', self.pysat_keys) + self.pysat_keys[self.pysat_keys.index("r_corr")] = None + self.test_ocb_added() + return + + def test_cust_add_ocb_to_data_vect(self): + """Test adding OCB vector data to pysat with load.""" + + # Load the boundaries + self.load_boundaries() + + # Update the custom variables + self.cust_kwargs['ocb'] = self.ocb + + # Cycle through the different custom inputs + for kw, val in [(['evar_names', 'vector_names'], + [['vect_evar'], + {'vect_evar': {'aacgm_n': self.pysat_var2, + 'aacgm_e': self.pysat_var2, + 'dat_name': 'vect', + 'dat_units': 'm/s'}}]), + (['curl_evar_names', 'vector_names'], + [['vect_cevar'], + {'vect_cevar': {'aacgm_n': self.pysat_var2, + 'aacgm_e': self.pysat_key, + 'dat_name': 'vect', + 'dat_units': 'm/s'}}]), + (['vector_names'], + [{'vect_cust': {'aacgm_n': self.pysat_key, + 'aacgm_e': self.pysat_var2, + 'dat_name': 'vect', 'dat_units': 'm/s', + 'scale_func': None}}]), + (['evar_names', 'curl_evar_names', 'vector_names'], + [[self.pysat_var2], [self.pysat_key], + {'vect_cust': {'aacgm_n': self.pysat_key, + 'aacgm_e': self.pysat_key, + 'dat_name': 'vect', 'dat_units': 'm/s', + 'scale_func': None}}])]: + # Ensure the record index is correct + self.ocb.rec_ind = self.rec_ind + + # Update the Instrument to include a custom function + self.pysat_kw['custom'] = [{'function': ocb_pysat.add_ocb_to_data, + 'kwargs': dict(self.cust_kwargs)}] + # Update the custom function kwargs + for i, kw_val in enumerate(kw): + self.pysat_kw['custom'][0]['kwargs'][kw_val] = val[i] + + with self.subTest(cust_kwargs=self.pysat_kw['custom'][0]['kwargs']): + # Load and test the defaults + self.test_load() + + # Test the additional data + self.added_keys = [kk for kk in self.test_inst.meta.keys() + if kk.find('_ocb') > 0] + self.pysat_keys = list() + for aa in self.added_keys: + pp = aa.split("_ocb")[0] + if pp == "r_corr": + pp = None + elif pp not in self.test_inst.variables: + pp = self.pysat_key + self.pysat_keys.append(pp) + self.test_ocb_added() return def test_cust_add_ocb_to_data_no_file(self): """Test adding ocb to pysat with load using no OCB file or data.""" - del self.cust_kwargs['ocb'] + # Update the custom kwargs self.cust_kwargs['ocbfile'] = None self.cust_kwargs['instrument'] = 'image' self.cust_kwargs['hemisphere'] = 1 - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) + # Update the Instrument to include a custom function + self.pysat_kw['custom'] = [{'function': ocb_pysat.add_ocb_to_data, + 'kwargs': self.cust_kwargs}] + + # Test the defaults self.test_load() + # Test the logging output self.lwarn = u'no data in Boundary file(s)' - self.lout = self.log_capture.getvalue() - self.assertTrue(self.lout.find(self.lwarn) >= 0) - return - - def test_cust_add_ocb_to_data_bad_mlat(self): - """Test failure of unknown mlat key in add_ocb_to_data custom func.""" - - with self.assertRaisesRegex(ValueError, - 'unknown magnetic latitude name bad'): - ocb_pysat.add_ocb_to_data(self.test_inst, "bad", "mlt", - ocb=self.ocb) - - def test_cust_add_ocb_to_data_bad_mlt(self): - """Test failure of unknown mlt key in add_ocb_to_data custom func.""" - self.cust_kwargs['mlt_name'] = 'bad' - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - with self.assertRaisesRegex(ValueError, - 'unknown magnetic local time name bad'): - self.test_load() - return - - def test_cust_add_ocb_to_data_bad_evar(self): - """Test failure of unknown E field key in custom func.""" - self.cust_kwargs['evar_names'] = ['bad'] - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) - - with self.assertRaisesRegex(ValueError, - 'at least one unknown E field name'): - self.test_load() + self.eval_logging_message() return - def test_cust_add_ocb_to_data_bad_curl(self): - """Test failure of unknown E field key in custom func.""" - self.cust_kwargs['curl_evar_names'] = ['bad'] - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) + def test_cust_add_ocb_to_data_bad_inputs(self): + """Test failure of unknown inputs in add_ocb_to_data custom func.""" + # Cycle through different bad inputs + for kw, msg in [('mlt_name', 'unknown magnetic local time name bad'), + ('evar_names', 'at least one unknown E field name'), + ('curl_evar_names', + 'at least one unknown E field name')]: + # Update the input kwargs + self.pysat_kw['custom'] = [{'function': ocb_pysat.add_ocb_to_data, + 'kwargs': dict(self.cust_kwargs)}] + self.pysat_kw['custom'][0]['kwargs'][kw] = 'bad' - with self.assertRaisesRegex(ValueError, - 'at least one unknown E field name'): - self.test_load() + with self.subTest(cust_kwargs=self.pysat_kw['custom'][0]['kwargs']): + # Test the failure when loading data + with self.assertRaisesRegex(ValueError, msg): + self.test_load() return def test_cust_add_ocb_to_data_bad_vector_scale(self): @@ -1231,8 +1026,8 @@ def test_cust_add_ocb_to_data_bad_vector_scale(self): 'aacgm_e': 'bad_e', 'dat_name': 'bad', 'dat_units': ''}} - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) + self.pysat_kw['custom'] = [{'function': ocb_pysat.add_ocb_to_data, + 'kwargs': self.cust_kwargs}] with self.assertRaisesRegex(ValueError, 'missing scaling function for bad'): @@ -1247,8 +1042,8 @@ def test_cust_add_ocb_to_data_bad_vector_name(self): 'aacgm_e': self.pysat_key, 'dat_name': 'bad', 'dat_units': ''}} - self.test_inst.custom_attach(ocb_pysat.add_ocb_to_data, - kwargs=self.cust_kwargs) + self.pysat_kw['custom'] = [{'function': ocb_pysat.add_ocb_to_data, + 'kwargs': self.cust_kwargs}] with self.assertRaisesRegex(ValueError, 'unknown vector name bad_n'): self.test_load() @@ -1261,48 +1056,11 @@ class TestPysatCustMethodsEAB(TestPysatCustMethods): def setUp(self): """Initialize the unit tests for using the pysat.Custom methods.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 - - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_eab") - self.assertTrue(path.isfile(self.test_file)) - self.ocb = ocbpy.EABoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.test_inst = pysat.Instrument('pysat', 'testing', num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - self.pysat_var2 = 'dummy2' - self.cust_kwargs = {'mlat_name': self.pysat_lat, 'mlt_name': 'mlt', - 'ocb': self.ocb, 'max_sdiff': self.del_time} - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) + # Initalize the defaults + super().setUp() - return - - def tearDown(self): - """Clean the test environment.""" - - del self.ocb_key, self.pysat_key, self.notes, self.del_time - del self.added_keys, self.pysat_keys, self.test_inst, self.ocb - del self.test_file, self.log_capture, self.cust_kwargs, self.pysat_var2 - del self.lwarn, self.lout, self.pysat_lat + # Update the class defaults + self.ocb_class = ocbpy.EABoundary return @@ -1314,52 +1072,17 @@ class TestPysatCustMethodsDual(TestPysatCustMethods): def setUp(self): """Initialize the unit tests for using the pysat.Custom methods.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 - - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb = ocbpy.DualBoundary(ocb_filename=self.test_file, - ocb_instrument='image', - eab_filename=self.test_file.replace( - 'north_circle', 'north_eab'), - eab_instrument='image', - hemisphere=1) - self.ocb.rec_ind = 0 - - self.test_inst = pysat.Instrument('pysat', 'testing', num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - self.pysat_var2 = 'dummy2' - self.cust_kwargs = {'mlat_name': self.pysat_lat, 'mlt_name': 'mlt', - 'ocb': self.ocb, 'max_sdiff': self.del_time} - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) + # Initalize the defaults + super().setUp() - return - - def tearDown(self): - """Clean the test environment.""" - - del self.ocb_key, self.pysat_key, self.notes, self.pysat_lat - del self.added_keys, self.pysat_keys, self.test_inst, self.ocb - del self.test_file, self.log_capture, self.cust_kwargs, self.pysat_var2 - del self.lwarn, self.lout, self.del_time + # Update the class defaults + self.ocb_class = ocbpy.DualBoundary + self.ocb_kw = {'ocb_filename': self.test_file, + 'ocb_instrument': 'image', + 'eab_filename': self.test_file.replace('north_circle', + 'north_eab'), + 'eab_instrument': 'image', 'hemisphere': 1} + self.rec_ind = 0 return @@ -1371,105 +1094,15 @@ class TestPysatCustMethodsXarray(TestPysatCustMethods): def setUp(self): """Initialize the unit tests for using the pysat.Custom methods.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 + # Initalize the defaults + super().setUp() - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb = ocbpy.OCBoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.test_inst = pysat.Instrument('pysat', 'testing_xarray', - num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - self.pysat_var2 = 'dummy2' - self.cust_kwargs = {'mlat_name': self.pysat_lat, 'mlt_name': 'mlt', - 'ocb': self.ocb, 'max_sdiff': self.del_time} - # Reduce pysat warnings - load_kwargs = {'date': self.ocb.dtime[self.ocb.rec_ind]} - if version.Version(pysat.__version__) > version.Version('3.0.1'): - load_kwargs['use_header'] = True - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - - return - - def tearDown(self): - """Clean the test environment.""" - - del self.ocb_key, self.pysat_key, self.notes, self.lwarn, self.lout - del self.added_keys, self.pysat_keys, self.test_inst, self.ocb - del self.test_file, self.log_capture, self.cust_kwargs, self.pysat_var2 - del self.del_time, self.pysat_lat - return - - -@unittest.skipIf(no_pysat, "pysat not installed") -class TestPysatCustMethods2DXarray(TestPysatCustMethods): - """Integration tests for using ocbpy with pysat 2D Xarray data.""" - - def setUp(self): - """Initialize the tests for using the pysat.Custom methods.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy1' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 - - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb = ocbpy.OCBoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.test_inst = pysat.Instrument('pysat', 'testing2d_xarray', - num_samples=50400, - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - self.pysat_var2 = 'dummy2' - self.cust_kwargs = {'mlat_name': self.pysat_lat, 'mlt_name': 'mlt', - 'ocb': self.ocb, 'max_sdiff': self.del_time} - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - - return - - def tearDown(self): - """Clean the test environment.""" - - del self.ocb_key, self.pysat_key, self.notes, self.lwarn, self.lout - del self.added_keys, self.pysat_keys, self.test_inst, self.ocb - del self.test_file, self.log_capture, self.cust_kwargs, self.pysat_var2 - del self.del_time, self.pysat_lat + # Update the class defaults + # TODO(#130) remove version checking by updating minimum supported pysat + if version.Version(pysat.__version__) < version.Version('3.1.0'): + self.test_module = pysat.instruments.pysat_testing2d_xarray + else: + self.test_module = pysat.instruments.pysat_ndtesting return @@ -1479,46 +1112,9 @@ class TestPysatCustMethodsModel(TestPysatCustMethods): def setUp(self): """Initialize the tests for using the pysat.Custom methods.""" - # Set the utility defaults - self.ocb_key = 'ocb_test' - self.pysat_key = 'dummy2' - self.pysat_lat = 'latitude' - self.notes = None - self.added_keys = list() - self.pysat_keys = list() - self.del_time = 600 - - # Set the method defaults - self.test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.assertTrue(path.isfile(self.test_file)) - self.ocb = ocbpy.OCBoundary(self.test_file, instrument='image', - hemisphere=1) - self.ocb.rec_ind = 27 - - self.test_inst = pysat.Instrument('pysat', 'testmodel', - clean_level='clean', - update_files=True, - file_date_range=pds.date_range( - self.ocb.dtime[0], - self.ocb.dtime[-1], freq='1D')) - self.pysat_var2 = 'dummy2' - self.cust_kwargs = {'mlat_name': self.pysat_lat, 'mlt_name': 'mlt', - 'ocb': self.ocb, 'max_sdiff': self.del_time} - - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - - return - - def tearDown(self): - """Clean the test environment.""" + # Initalize the defaults + super().setUp() - del self.ocb_key, self.pysat_key, self.notes, self.lwarn, self.lout - del self.added_keys, self.pysat_keys, self.test_inst, self.ocb - del self.test_file, self.log_capture, self.cust_kwargs, self.pysat_var2 - del self.del_time, self.pysat_lat + # Update the class defaults + self.test_module = pysat.instruments.pysat_testmodel return From b7c39d220becdff95af8f568dc5ad5e8c80c8d9e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 26 Mar 2024 17:53:51 -0400 Subject: [PATCH 17/73] DOC: updated docstrings for time tests Updated the time unit test docstring formats. --- ocbpy/tests/test_ocb_time.py | 110 +++++++++++++++++------------------ 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/ocbpy/tests/test_ocb_time.py b/ocbpy/tests/test_ocb_time.py index 40545740..4f4f9c92 100644 --- a/ocbpy/tests/test_ocb_time.py +++ b/ocbpy/tests/test_ocb_time.py @@ -13,8 +13,10 @@ class TestOCBTimeMethods(unittest.TestCase): + """Unit tests for time functions.""" + def setUp(self): - """ Set up test runs """ + """Set up test runs.""" self.dtime = dt.datetime(2001, 1, 1) self.dtime2 = dt.datetime(1901, 1, 1) @@ -25,28 +27,26 @@ def tearDown(self): del self.dtime, self.dtime2 def test_year_soy_to_datetime(self): - """ Test to see that the seconds of year conversion works - """ + """Test to see that the seconds of year conversion works.""" self.assertEqual(ocb_time.year_soy_to_datetime(2001, 0), self.dtime) def test_convert_time_date_tod(self): - """ Test to see that the default datetime construction works - """ + """Test to see that the default datetime construction works.""" # Test the default date implimentation self.assertEqual(ocb_time.convert_time(date="2001-01-01", tod="00:00:00"), self.dtime) def test_convert_time_date_tod_uncoverted(self): - """ Test the datetime construction with unconverted data - """ + """Test the datetime construction with unconverted data.""" # Test the default date implimentation self.assertEqual(ocb_time.convert_time(date="2001-01-01", tod="00:00:00.000001"), self.dtime) def test_convert_time_date_tod_fmt(self): - """ Test to see that the datetime construction works with custom format + """Test to see that the datetime construction works with custom format. + """ # Test the custom date implimentation self.assertEqual( @@ -55,52 +55,49 @@ def test_convert_time_date_tod_fmt(self): self.dtime) def test_convert_time_year_soy(self): - """ Test to see that the datetime construction works with year-soy - """ + """Test to see that the datetime construction works with year-soy.""" # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(year=2001, soy=0), self.dtime) def test_convert_time_yyddd_tod(self): - """ Test to see that the datetime construction works with yyddd and tod + """Test to see that the datetime construction works with yyddd and tod. + """ # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", tod="00:00:00"), self.dtime) def test_convert_time_yyddd_tod_w_fmt(self): - """ Test the datetime construction with yyddd, tod, and datetime_fmt - """ + """Test the datetime construction with yyddd, tod, and datetime_fmt.""" # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", tod="00 00 00", datetime_fmt="YYDDD %H %M %S"), self.dtime) def test_convert_time_yyddd_tod_w_time_fmt(self): - """ Test the datetime construction with yyddd, tod, and time fmt - """ + """Test the datetime construction with yyddd, tod, and time fmt.""" # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", tod="00 00 00", datetime_fmt="%H %M %S"), self.dtime) def test_convert_time_yyddd_sod(self): - """Test to see that the datetime construction works with yyddd and sod + """Test to see that the datetime construction works with yyddd and sod. + """ # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", sod=0), self.dtime) def test_convert_time_yyddd_sod_ms(self): - """ Test the datetime construction works with yyddd, sod, and ms - """ + """Test the datetime construction works with yyddd, sod, and msec.""" self.dtime = self.dtime.replace(microsecond=1) # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", sod=1.0e-6), self.dtime) def test_convert_time_dict_input(self): - """ Test to see that the datetime construction works with dict inputs - """ + """Test to see that the datetime construction works with dict inputs.""" # Test dictionary input implimentation input_dict = {"year": None, "soy": None, "yyddd": None, "sod": None, "date": "2001-01-01", "tod": "000000", @@ -115,130 +112,129 @@ def test_convert_time_dict_input(self): del input_dict def test_convert_time_failure_yyddd(self): - """ Test convert_time failure with non-string input for yyddd - """ + """Test convert_time failure with non-string input for yyddd.""" with self.assertRaisesRegex(ValueError, "YYDDD must be a string"): ocb_time.convert_time(yyddd=101001) def test_convert_time_failure_soy(self): - """ Test convert_time failure with bad input for year-soy - """ + """Test convert_time failure with bad input for year-soy.""" with self.assertRaisesRegex(ValueError, "does not match format"): ocb_time.convert_time(soy=200) def test_convert_time_failure_bad_date_fmt(self): - """ Test convert_time failure with bad input for incorrect date format - """ + """Test convert_time failure with incorrect date format.""" with self.assertRaisesRegex(ValueError, "does not match format"): ocb_time.convert_time(date="2000", tod="00") def test_yyddd_to_date(self): - """ Test to see that the datetime construction works - """ + """Test to see that the datetime construction works.""" # Test the year-soy implimentation for 2001 and 1901 self.assertEqual(ocb_time.yyddd_to_date(yyddd="101001"), self.dtime) self.assertEqual(ocb_time.yyddd_to_date(yyddd="01001"), self.dtime2) def test_yyddd_to_date_failure(self): - """ Test yyddd_to_date failure with non-string input - """ + """Test yyddd_to_date failure with non-string input.""" with self.assertRaisesRegex(ValueError, "YYDDD must be a string"): ocb_time.yyddd_to_date(yyddd=101001) def test_datetime2hr(self): - """ Test datetime to hour of day conversion""" + """Test datetime to hour of day conversion.""" self.assertEqual(ocb_time.datetime2hr(self.dtime), 0.0) def test_datetime2hr_all_fracs(self): - """ Test datetime to hour of day conversion for a time with h,m,s,ms""" + """Test datetime to hour of day conversion for a time with h,m,s,ms.""" self.dtime = self.dtime.replace(hour=1, minute=1, second=1, microsecond=1) self.assertAlmostEqual(ocb_time.datetime2hr(self.dtime), 1.01694444472) def test_datetime2hr_input_failure(self): - """ Test datetime to hour of day conversion with bad input""" + """Test datetime to hour of day conversion with bad input.""" with self.assertRaises(AttributeError): ocb_time.datetime2hr(5.0) class TestOCBTimeUnits(unittest.TestCase): + """Class for testing the time unit conversion functions.""" + def setUp(self): - """ Set up test runs """ + """Set up test runs.""" self.lon = np.linspace(0.0, 360.0, 37) self.lt = np.linspace(0.0, 24.0, 37) self.out = None def tearDown(self): - """ Clean up after each test """ + """Clean up after each test.""" del self.lon, self.lt, self.out def test_deg2hr_array(self): - """ Test degree to hour conversion for an array""" + """Test degree to hour conversion for an array.""" self.out = ocb_time.deg2hr(self.lon) for i, val in enumerate(self.lt): self.assertAlmostEqual(self.out[i], val) def test_deg2hr_value(self): - """ Test degree to hour conversion for a single value""" + """Test degree to hour conversion for a single value.""" self.out = ocb_time.deg2hr(self.lon[0]) self.assertAlmostEqual(self.out, self.lt[0]) def test_hr2deg_array(self): - """ Test hour to degree conversion for an array""" + """Test hour to degree conversion for an array.""" self.out = ocb_time.hr2deg(self.lt) for i, val in enumerate(self.lon): self.assertAlmostEqual(self.out[i], val) def test_hr2deg_value(self): - """ Test hour to degree conversion for a single value""" + """Test hour to degree conversion for a single value.""" self.out = ocb_time.deg2hr(self.lt[0]) self.assertAlmostEqual(self.out, self.lon[0]) def test_hr2rad_array(self): - """ Test hour to radian conversion for an array""" + """Test hour to radian conversion for an array.""" self.out = ocb_time.hr2rad(self.lt) for i, val in enumerate(np.radians(self.lon)): self.assertAlmostEqual(self.out[i], val) def test_hr2rad_value(self): - """ Test hour to radian conversion for a single value""" + """Test hour to radian conversion for a single value.""" self.out = ocb_time.hr2rad(self.lt[0]) self.assertAlmostEqual(self.out, np.radians(self.lon[0])) def test_rad2hr_array(self): - """ Test radian to hour conversion for an array""" + """Test radian to hour conversion for an array.""" self.out = list(ocb_time.rad2hr(np.radians(self.lon))) for i, val in enumerate(self.out): self.assertAlmostEqual(val, self.lt[i]) def test_rad2hr_value(self): - """ Test radian to hour conversion for a single value""" + """Test radian to hour conversion for a single value.""" self.out = ocb_time.rad2hr(np.radians(self.lon[0])) self.assertAlmostEqual(self.out, self.lt[0]) class TestOCBGeographicTime(unittest.TestCase): + """Unit tests for geographic time conversions.""" + def setUp(self): - """ Set up test runs """ + """Set up test runs.""" self.dtime = dt.datetime(2001, 1, 1, 1) self.lon = [390.0, 359.0, 90.0, -15.0, -30.0] self.lt = [27.0, 0.9333333333333336, 7.0, 0.0, -1.0] self.out = list() def tearDown(self): - """ Clean up after each test """ + """Clean up after each test.""" del self.lon, self.lt, self.dtime, self.out def test_glon2slt(self): - """ Test longitude to slt conversion for a range of values""" + """Test longitude to slt conversion for a range of values.""" # Prepare test output self.out = np.array(self.lt) self.out[self.out >= 24.0] -= 24.0 @@ -250,7 +246,7 @@ def test_glon2slt(self): self.out[i]) def test_slt2glon(self): - """ Test slt to longitude conversion for a range of values""" + """Test slt to longitude conversion for a range of values.""" # Prepare test output self.out = np.array(self.lon) self.out[self.out > 180.0] -= 360.0 @@ -261,7 +257,7 @@ def test_slt2glon(self): self.out[i]) def test_slt2glon_list(self): - """ Test slt to longitude conversion for a list of values""" + """Test slt to longitude conversion for a list of values.""" # Prepare test output self.lon = np.asarray(self.lon) self.lon[self.lon > 180.0] -= 360.0 @@ -273,7 +269,7 @@ def test_slt2glon_list(self): self.assertAlmostEqual(ll, self.lon[i]) def test_slt2glon_array(self): - """ Test slt to longitude conversion for an array of values""" + """Test slt to longitude conversion for an array of values.""" # Prepare test output self.lon = np.asarray(self.lon) self.lon[self.lon > 180.0] -= 360.0 @@ -285,7 +281,7 @@ def test_slt2glon_array(self): self.assertAlmostEqual(ll, self.lon[i]) def test_glon2slt_list(self): - """ Test longtiude to lt conversion with list input""" + """Test longtiude to lt conversion with list input.""" # Prepare test output self.lt = np.asarray(self.lt) self.lt[self.lt >= 24.0] -= 24.0 @@ -297,7 +293,7 @@ def test_glon2slt_list(self): self.assertAlmostEqual(ll, self.lt[i]) def test_glon2slt_array(self): - """ Test longtiude to lt conversion with array input""" + """Test longtiude to lt conversion with array input.""" # Prepare test output self.lt = np.asarray(self.lt) self.lt[self.lt >= 24.0] -= 24.0 @@ -310,8 +306,10 @@ def test_glon2slt_array(self): class TestTimeFormatMethods(unittest.TestCase): + """Unit tests for time formatting functions.""" + def setUp(self): - """ Set up test runs """ + """Set up test runs.""" self.dtime = dt.datetime(2001, 1, 1) self.dt_formats = ['No Directives', '%y-%m-%d %H:%M:%S', '%a %b %Y %f', '%A %B %z %Z', '%c', '%j %x', '%X'] @@ -339,8 +337,10 @@ def test_get_datetime_fmt_len(self): class TestFixRange(unittest.TestCase): + """Unit tests for fixing the range of different variables.""" + def setUp(self): - """ Set up test runs """ + """Set up test runs.""" self.vals = np.linspace(-190.0, 360.0, 37) self.out = None @@ -351,12 +351,12 @@ def tearDown(self): del self.vals, self.out def test_fix_range_max_min_failure(self): - """Test fix_range failure with bad max/min input""" + """Test fix_range failure with bad max/min input.""" with self.assertRaisesRegex(ValueError, "Minimum is not less than"): ocb_time.fix_range(self.vals, 10.0, -10.0) def test_fix_range_range_failure(self): - """Test fix_range failure with bad value range input""" + """Test fix_range failure with bad value range input.""" with self.assertRaisesRegex(ValueError, "Value range must be greater"): ocb_time.fix_range(self.vals, -10.0, 10.0, 0.0) From 35e5b513f1a7c7f8fdacfa0f7290d822a1481caa Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 26 Mar 2024 17:59:12 -0400 Subject: [PATCH 18/73] STY: updated scaling tests Updated the scaling unit tests to use the common testing classes and variables. --- ocbpy/tests/test_ocb_scaling.py | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/ocbpy/tests/test_ocb_scaling.py b/ocbpy/tests/test_ocb_scaling.py index 7356eda1..b5439413 100644 --- a/ocbpy/tests/test_ocb_scaling.py +++ b/ocbpy/tests/test_ocb_scaling.py @@ -5,7 +5,6 @@ # ----------------------------------------------------------------------------- """Tests the ocb_scaling class and functions.""" -from io import StringIO import logging import numpy from numpy import nan # noqa F401 @@ -13,18 +12,16 @@ import unittest import ocbpy +import ocbpy.tests.class_common as cc -class TestOCBScalingLogFailure(unittest.TestCase): +class TestOCBScalingLogFailure(cc.TestLogWarnings): """Unit tests for logging messages in ocb_scaling module.""" def setUp(self): """Initialize the test class.""" - # Initialize the logging info - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) + # Update the logging info + super().setUp() ocbpy.logger.setLevel(logging.INFO) # Initialize the testing variables @@ -42,7 +39,8 @@ def setUp(self): def tearDown(self): """Tear down the test case.""" - del self.lwarn, self.lout, self.log_capture, self.ocb, self.vdata + super.tearDown() + del self.ocb, self.vdata return def test_no_scale_func(self): @@ -53,9 +51,8 @@ def test_no_scale_func(self): self.vdata.set_ocb(self.ocb) self.assertIsNone(self.vdata.scale_func) - self.lout = self.log_capture.getvalue() # Test logging error message for each bad initialization - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() return def test_inconsistent_vector_warning(self): @@ -69,9 +66,8 @@ def test_inconsistent_vector_warning(self): dat_name="Test", dat_units="$m s^{-1}$") - self.lout = self.log_capture.getvalue() # Test logging error message for each bad initialization - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() return @@ -603,13 +599,10 @@ class TestDualScalingMethods(TestOCBScalingMethods): def setUp(self): """Initialize the DualBoundary and VectorData objects.""" - test_dir = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data") - self.assertTrue(path.isdir(test_dir)) self.ocb = ocbpy.DualBoundary( - eab_filename=path.join(test_dir, "test_north_eab"), + eab_filename=path.join(cc.test_dir, "test_north_eab"), eab_instrument='image', ocb_instrument='image', hemisphere=1, - ocb_filename=path.join(test_dir, "test_north_circle")) + ocb_filename=path.join(cc.test_dir, "test_north_circle")) self.ocb_attrs = ['ocb_lat', 'ocb_mlt', 'r_corr', 'ocb_n', 'ocb_e', 'ocb_z'] @@ -736,8 +729,7 @@ class TestVectorDataRaises(unittest.TestCase): def setUp(self): """Initialize the tests for calc_vec_pole_angle.""" - test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") + test_file = path.join(cc.test_dir, "test_north_circle") self.assertTrue(path.isfile(test_file)) self.ocb = ocbpy.OCBoundary(filename=test_file, instrument='image') self.ocb.rec_ind = 27 @@ -1081,8 +1073,7 @@ class TestOCBScalingArrayMethods(unittest.TestCase): def setUp(self): """Set up the test environment.""" - test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") + test_file = path.join(cc.test_dir, "test_north_circle") self.ocb = ocbpy.OCBoundary(filename=test_file, instrument='image') # Construct a set of test vectors that have all the different OCB From e4989ecfcaad2efe654ae9d799088cfcf64b03d1 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 09:37:58 -0400 Subject: [PATCH 19/73] BUG: fixed bug in shape tests Fixed bug in shape tests that prevented indexes and single values being evaluated correctly. --- ocbpy/ocb_scaling.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/ocbpy/ocb_scaling.py b/ocbpy/ocb_scaling.py index cf7c3538..0648a5a5 100644 --- a/ocbpy/ocb_scaling.py +++ b/ocbpy/ocb_scaling.py @@ -295,13 +295,15 @@ def _test_update_vector_shape(self): """ # Get the required input shapes - vshapes = [self.aacgm_lat.shape, self.aacgm_mlt.shape, - self.dat_ind.shape, self.aacgm_n.shape, self.aacgm_e.shape, - self.aacgm_z.shape] - vshapes = np.unique(np.asarray(vshapes, dtype=object)) + vshapes = list() + for vshape in [self.aacgm_lat.shape, self.aacgm_mlt.shape, + self.dat_ind.shape, self.aacgm_n.shape, + self.aacgm_e.shape, self.aacgm_z.shape]: + if vshape not in vshapes: + vshapes.append(vshape) # Determine the desired shape - self.vshape = () if len(vshapes) == 0 else vshapes.max() + self.vshape = () if len(vshapes) == 0 else max(vshapes) # Evaluate for potential mismatched attributes if len(vshapes) > 2 or (len(vshapes) == 2 and min(vshapes) != ()): @@ -345,8 +347,9 @@ def _test_update_bound_shape(self): oshape = () if len(oshapes) == 0 else max(oshapes) - if(np.array(oshape).size != self.ocb_ind.size or len(oshapes) > 2 - or (len(oshapes) == 2 and len(min(oshapes)) > 0)): + if (self.ocb_ind.shape != () and (oshape == () or np.all( + oshape != self.ocb_ind.shape))) or len(oshapes) > 2 or ( + len(oshapes) == 2 and len(min(oshapes)) > 0): raise ValueError('OCB index and input shapes mismatched') # Compare and update the vector data shape if needed From f5c991049efa184c6c71dbeb49bd951d93656830 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 09:38:40 -0400 Subject: [PATCH 20/73] BUG: fixed bugs in test updates Fixed bug in common class calls and added the test_dir variable to more places. --- ocbpy/tests/test_ocb_scaling.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ocbpy/tests/test_ocb_scaling.py b/ocbpy/tests/test_ocb_scaling.py index b5439413..195ce74b 100644 --- a/ocbpy/tests/test_ocb_scaling.py +++ b/ocbpy/tests/test_ocb_scaling.py @@ -25,8 +25,7 @@ def setUp(self): ocbpy.logger.setLevel(logging.INFO) # Initialize the testing variables - test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") + test_file = path.join(cc.test_dir, "test_north_circle") self.assertTrue(path.isfile(test_file)) self.ocb = ocbpy.OCBoundary(filename=test_file, instrument='image') self.ocb.rec_ind = 27 @@ -39,7 +38,7 @@ def setUp(self): def tearDown(self): """Tear down the test case.""" - super.tearDown() + super().tearDown() del self.ocb, self.vdata return @@ -77,8 +76,7 @@ class TestOCBScalingMethods(unittest.TestCase): def setUp(self): """Initialize the OCBoundary and VectorData objects.""" - test_file = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") + test_file = path.join(cc.test_dir, "test_north_circle") self.assertTrue(path.isfile(test_file)) self.ocb = ocbpy.OCBoundary(filename=test_file, instrument='image') self.ocb.rec_ind = 27 From 7d22e8b540b82aa99188e16fda34dbf6ecfe4ea5 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 09:45:06 -0400 Subject: [PATCH 21/73] TST: updated general tests Updated the general tests to use the common test class and variable. --- ocbpy/tests/test_general.py | 64 +++++++++++++------------------------ 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/ocbpy/tests/test_general.py b/ocbpy/tests/test_general.py index c5fc51ed..cb99b298 100644 --- a/ocbpy/tests/test_general.py +++ b/ocbpy/tests/test_general.py @@ -6,31 +6,22 @@ """Tests the general instrument sub-module.""" import datetime as dt -import logging -from io import StringIO import os -import unittest -import ocbpy import ocbpy.instruments.general as ocb_igen +import ocbpy.tests.class_common as cc -class TestGeneralFileTestFunctions(unittest.TestCase): +class TestGeneralFileTestFunctions(cc.TestLogWarnings): """Unit tests for the general file functions.""" def setUp(self): """Set up the test environment.""" - self.test_file = os.path.join(os.path.dirname(ocbpy.__file__), "tests", - "test_data", "test_north_circle") - self.temp_output = os.path.join(os.path.dirname(ocbpy.__file__), - "tests", "test_data", "temp_gen") - self.rstat = None + super().setUp() - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) + self.test_file = os.path.join(cc.test_dir, "test_north_circle") + self.temp_output = os.path.join(cc.test_dir, "temp_gen") + self.rstat = None return def tearDown(self): @@ -38,8 +29,8 @@ def tearDown(self): if os.path.isfile(self.temp_output): os.remove(self.temp_output) - del self.test_file, self.lwarn, self.lout, self.log_capture, self.rstat - del self.temp_output + del self.test_file, self.rstat, self.temp_output + super().tearDown() return def test_file_test_success(self): @@ -53,9 +44,8 @@ def test_file_test_not_file(self): self.lwarn = u"name provided is not a file" self.rstat = ocb_igen.test_file("/") - self.lout = self.log_capture.getvalue() - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() self.assertFalse(self.rstat) return @@ -66,9 +56,8 @@ def test_file_test_empty_file(self): # Create an empty file and read it in open(self.temp_output, 'a').close() self.rstat = ocb_igen.test_file(self.temp_output) - self.lout = self.log_capture.getvalue() - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() self.assertFalse(self.rstat) return @@ -81,26 +70,22 @@ def test_large_file(self): fout.truncate(2024 * 1024 * 1024) self.rstat = ocb_igen.test_file(self.temp_output) - self.lout = self.log_capture.getvalue() - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() self.assertFalse(self.rstat) return -class TestGeneralLoadFunctions(unittest.TestCase): +class TestGeneralLoadFunctions(cc.TestLogWarnings): """Unit tests for the general loading functions.""" def setUp(self): """Set up a clean test environment.""" + super().setUp() - ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_file_soy = os.path.join(ocb_dir, "tests", "test_data", - "test_north_circle") - self.test_file_dt = os.path.join(ocb_dir, "tests", "test_data", - "dmsp-ssj_north_out.ocb") - self.test_file_sod = os.path.join(ocb_dir, "tests", "test_data", - "test_sod") + self.test_file_soy = os.path.join(cc.test_dir, "test_north_circle") + self.test_file_dt = os.path.join(cc.test_dir, "dmsp-ssj_north_out.ocb") + self.test_file_sod = os.path.join(cc.test_dir, "test_sod") self.headers = {self.test_file_soy: [u"YEAR SOY NB PHICENT RCENT R A RERR FOM"], self.test_file_sod: @@ -128,18 +113,14 @@ def setUp(self): 'max_str_length': 50, 'header': list()} self.out = None - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) return def tearDown(self): """Clean up the test environment.""" - del self.test_file_soy, self.lwarn, self.lout, self.log_capture - del self.test_file_dt, self.headers, self.out, self.test_out - del self.load_kwargs + super().tearDown() + + del self.test_file_soy, self.test_file_dt, self.headers, self.out + del self.test_out, self.load_kwargs return def test_load_ascii_data_badfile(self): @@ -161,11 +142,10 @@ def test_load_ascii_data_bad_header(self): self.out = ocb_igen.load_ascii_data(self.test_file_soy, 0, **self.load_kwargs) - self.lout = self.log_capture.getvalue() + + self.eval_logging_message() self.assertListEqual(self.out[0], []) self.assertDictEqual(self.out[1], {}) - - self.assertTrue(self.lout.find(self.lwarn) >= 0) return def test_load_ascii_data_w_header(self): From 767cf0ce1db5ef88cdd617545a33f9a0e43b4752 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 10:02:00 -0400 Subject: [PATCH 22/73] BUG: fixed error check pathlib behaves differently, added more checks to ensure correct behaviour. --- ocbpy/boundaries/files.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ocbpy/boundaries/files.py b/ocbpy/boundaries/files.py index ac1ccec6..43f8e167 100644 --- a/ocbpy/boundaries/files.py +++ b/ocbpy/boundaries/files.py @@ -26,7 +26,8 @@ def get_boundary_directory(): boundary_dir = os.path.join(str(pathlib.Path( ocbpy.boundaries.__file__).resolve().parent)) - if not os.path.isdir(boundary_dir): + if not os.path.isdir(boundary_dir) or boundary_dir.find( + "ocbpy") < 0 or boundary_dir.find("boundaries") < 0: raise OSError("can't find the OCBpy boundary file directory") return boundary_dir From a943868903b00f121dd165edc7068223cbbfe59b Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 10:02:28 -0400 Subject: [PATCH 23/73] STY: updated files tests Updated the files tests to use common classes and variables. --- ocbpy/tests/test_files.py | 37 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/ocbpy/tests/test_files.py b/ocbpy/tests/test_files.py index d62b162b..eed95b4a 100644 --- a/ocbpy/tests/test_files.py +++ b/ocbpy/tests/test_files.py @@ -6,22 +6,22 @@ """Tests the boundaries.files functions.""" import datetime as dt -from io import StringIO -import logging import os import unittest import ocbpy from ocbpy.boundaries import files +import ocbpy.tests.class_common as cc -class TestDMSPFileMethods(unittest.TestCase): +class TestDMSPFileMethods(cc.TestLogWarnings): """"Unit tests for the DMSP SSJ file routines.""" def setUp(self): """Initialize the test case by copying over necessary files.""" - self.test_dmsp = os.path.join(os.path.dirname(ocbpy.__file__), "tests", - "test_data", "dmsp-ssj_north_out.ocb") + super().setUp() + + self.test_dmsp = os.path.join(cc.test_dir, "dmsp-ssj_north_out.ocb") self.temp_files = [os.path.join(files.get_boundary_directory(), "dmsp-ssj_north_out1.ocb"), os.path.join(files.get_boundary_directory(), @@ -35,12 +35,6 @@ def setUp(self): "etime": dt.datetime(2011, 1, 1)} self.out = list() self.tfile = u'' - - self.lwarn = u'' - self.lout = u'' - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) return def tearDown(self): @@ -50,7 +44,8 @@ def tearDown(self): os.remove(self.tfile) del self.tfile, self.temp_files, self.test_dmsp, self.comp_dict - del self.out, self.lwarn, self.lout, self.log_capture + del self.out + super().tearDown() return def test_no_short_name_one_file(self): @@ -103,10 +98,9 @@ def test_bad_unknown_inst_file(self): # Get the default file and instrument self.out = files.get_boundary_files() - self.lout = self.log_capture.getvalue() # Test logging error message and data output - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.eval_logging_message() self.assertTrue(self.tfile in self.out.keys()) self.assertListEqual( sorted([kk for kk in self.out[self.tfile].keys()]), @@ -136,7 +130,6 @@ class TestFilesMethods(unittest.TestCase): def setUp(self): """Initialize the test class.""" self.out = None - self.orig_file = ocbpy.__file__ self.comp_dict = {'ocb': {'amp_north_radii.ocb': {'instrument': 'amp', 'hemisphere': 1, 'stime': dt.datetime(2010, 1, 1, 0, 0), @@ -199,11 +192,8 @@ def setUp(self): def tearDown(self): """Clean the test environment.""" - if ocbpy.__file__ != self.orig_file: - ocbpy.__file__ = self.orig_file - - del self.out, self.orig_file, self.comp_dict, self.short_to_long - del self.inst, self.long_to_short, self.hemi, self.ikey, self.fname + del self.out, self.comp_dict, self.short_to_long, self.fname + del self.inst, self.long_to_short, self.hemi, self.ikey return def test_get_boundary_directory(self): @@ -214,9 +204,12 @@ def test_get_boundary_directory(self): def test_get_boundary_directory_failure(self): """Test the failure of the default boundary directory definition.""" - ocbpy.__file__ = "/fake_dir/test_file" + good_location = str(ocbpy.boundaries.__file__) + ocbpy.boundaries.__file__ = "/fake_dir/test_file" with self.assertRaisesRegex(OSError, "boundary file directory"): - files.get_boundary_directory() + ocbpy.boundaries.files.get_boundary_directory() + + ocbpy.boundaries.__file__ = good_location return def test_get_boundary_files_unknown_boundary(self): From 08bd5dd36f397f48dcbf06d254cacd1e31a02e0d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 12:03:43 -0400 Subject: [PATCH 24/73] STY: use unittest method Use the appropriate unittest method for evaluating strings. --- ocbpy/tests/class_common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ocbpy/tests/class_common.py b/ocbpy/tests/class_common.py index 5c5017c0..1f165586 100644 --- a/ocbpy/tests/class_common.py +++ b/ocbpy/tests/class_common.py @@ -41,5 +41,5 @@ def eval_logging_message(self): # Test logging error message and data output self.lout = self.log_capture.getvalue() - self.assertTrue(self.lout.find(self.lwarn) >= 0) + self.assertRegex(self.lout, self.lwarn) return From 8a4a778c64a5996a2f9c1996738d18bcc3ee979e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 12:04:29 -0400 Subject: [PATCH 25/73] STY: update dmsp_ssj to use common test elements Updated the DMSP SSJ unit tests to use the common test class and variable. --- ocbpy/tests/test_dmsp_ssj_files.py | 121 ++++++++++++++--------------- 1 file changed, 59 insertions(+), 62 deletions(-) diff --git a/ocbpy/tests/test_dmsp_ssj_files.py b/ocbpy/tests/test_dmsp_ssj_files.py index 5223abea..fc7b2296 100644 --- a/ocbpy/tests/test_dmsp_ssj_files.py +++ b/ocbpy/tests/test_dmsp_ssj_files.py @@ -7,8 +7,6 @@ import datetime as dt from glob import glob -from io import StringIO -import logging import numpy as np import os import unittest @@ -17,6 +15,7 @@ import ocbpy from ocbpy import boundaries +import ocbpy.tests.class_common as cc no_ssj = False if hasattr(boundaries, 'dmsp_ssj_files') else True @@ -31,7 +30,7 @@ class TestSSJFetchDep(unittest.TestCase): def setUp(self): """Initialize the test class.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) + self.ocb_dir = os.path.split(os.path.split(cc.test_dir)[0])[0] self.sat_nums = [16, 17, 18] self.in_args = [dt.datetime(2010, 1, 1), dt.datetime(2010, 1, 2), os.path.join(self.ocb_dir, "tests", "test_data"), @@ -151,28 +150,25 @@ def test_fetch_ssj_files_failure_bad_sat(self): @unittest.skipIf(no_ssj, "ssj_auroral_boundary not installed, cannot test routines") -class TestSSJCreate(unittest.TestCase): +class TestSSJCreate(cc.TestLogWarnings): """Unit tests for `create_ssj_boundary_files`.""" def setUp(self): """Initialize the test class.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_dir = os.path.join(self.ocb_dir, "tests") + super().setUp() + + self.test_dir = os.path.split(cc.test_dir)[0] self.base_file = "".join(["dmsp-f16_ssj_precipitating-electrons-ions", "_20101231_v1.1.2"]) - self.comp_files = [os.path.join(self.test_dir, "test_data", + self.comp_files = [os.path.join(cc.test_dir, "{:s}_boundaries.csv".format( self.base_file))] - self.cdf_files = [os.path.join(self.test_dir, "test_data", + self.cdf_files = [os.path.join(cc.test_dir, '{:s}.cdf'.format(self.base_file))] self.out_cols = ['mlat', 'mlt'] self.out = list() self.eval_ref = self.comp_files[0] self.eval_out = None - self.lout = '' - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) return def tearDown(self): @@ -181,9 +177,10 @@ def tearDown(self): for ff in self.out: os.remove(ff) - del self.ocb_dir, self.out, self.test_dir, self.cdf_files - del self.comp_files, self.log_capture, self.lout, self.base_file - del self.out_cols, self.eval_ref, self.eval_out + del self.out, self.cdf_files, self.out_cols, self.eval_out + del self.comp_files, self.base_file, self.eval_ref, self.test_dir + + super().tearDown() return def eval_file_data(self, geo=False, mag=False, test_plot=False): @@ -271,20 +268,30 @@ def test_create_ssj_boundary_files_outcols_failure(self): self.cdf_files, out_dir=self.test_dir, out_cols=['fake']) return - def test_create_ssj_boundary_files_log_failure(self): - """Test create_ssj_boundary_files raising logging errors.""" + def test_create_ssj_boundary_files_log_cdf_failure(self): + """Test create_ssj_boundary_files raising CDF logging warning.""" - # Cycle through the different value error raises - for ii in [(self.comp_files, "CDF"), - ([self.test_dir], "bad input file")]: - with self.subTest(ii=list(ii)): - # Run with bad input file - self.out = dmsp_ssj_files.create_ssj_boundary_files(ii[0]) - self.assertEqual(len(self.out), 0) + # Run with bad input file + for fname in self.comp_files: + self.assertTrue(os.path.isfile(fname), + msg="missing test file: {:}".format(fname)) + self.out = dmsp_ssj_files.create_ssj_boundary_files(self.comp_files) + self.assertEqual(len(self.out), 0) + + # Test logging output + self.lwarn = "CDF" + self.eval_logging_message() + return + + def test_create_ssj_boundary_files_log_not_a_file_failure(self): + """Test create_ssj_boundary_files raising bad file logging warning.""" + # Run with bad input file + self.out = dmsp_ssj_files.create_ssj_boundary_files([cc.test_dir]) + self.assertEqual(len(self.out), 0) - # Test logging output - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, ii[1]) + # Test logging output + self.lwarn = "bad input file" + self.eval_logging_message() return def test_create_ssj_boundary_files_default(self): @@ -357,11 +364,10 @@ class TestSSJFetch(unittest.TestCase): def setUp(self): """Initialize the test class.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) + self.ocb_dir = os.path.split(os.path.split(cc.test_dir)[0])[0] self.sat_nums = [16, 17, 18] self.in_args = [dt.datetime(2010, 1, 1), dt.datetime(2010, 1, 2), - os.path.join(self.ocb_dir, "tests", "test_data"), - self.sat_nums] + cc.test_dir, self.sat_nums] self.fetch_files = list() return @@ -518,36 +524,33 @@ def test_eval_dmsp_bad_time_file(self): @unittest.skipIf(no_ssj, "ssj_auroral_boundary not installed, cannot test routines") -class TestSSJFormat(unittest.TestCase): +class TestSSJFormat(cc.TestLogWarnings): """Unit tests for `format_ssj_boundary_files`.""" def setUp(self): """Initialize the test class.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_dir = os.path.join(self.ocb_dir, "tests", "test_data") + super().setUp() + + self.test_dir = cc.test_dir self.comp_files = {"dmsp-ssj_north_20101231_20101231_v1.1.2.eab": - os.path.join(self.test_dir, + os.path.join(cc.test_dir, "dmsp-ssj_north_out.eab"), "dmsp-ssj_south_20101231_20101231_v1.1.2.eab": - os.path.join(self.test_dir, + os.path.join(cc.test_dir, "dmsp-ssj_south_out.eab"), "dmsp-ssj_north_20101231_20101231_v1.1.2.ocb": - os.path.join(self.test_dir, + os.path.join(cc.test_dir, "dmsp-ssj_north_out.ocb"), "dmsp-ssj_south_20101231_20101231_v1.1.2.ocb": - os.path.join(self.test_dir, + os.path.join(cc.test_dir, "dmsp-ssj_south_out.ocb")} - self.csv_files = [os.path.join(self.test_dir, + self.csv_files = [os.path.join(cc.test_dir, "".join(["dmsp-f16_ssj_precipitating", "-electrons-ions_20101231_", "v1.1.2_boundaries.csv"]))] self.out = list() self.ldtype = [int, '|U50', '|U50', float, float, float, float, float, float, float, float] - self.lout = '' - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) return def tearDown(self): @@ -556,8 +559,9 @@ def tearDown(self): for fout in self.out: os.remove(fout) - del self.ocb_dir, self.out, self.test_dir, self.csv_files - del self.log_capture, self.lout, self.comp_files, self.ldtype + del self.out, self.test_dir, self.csv_files, self.comp_files + del self.ldtype + super().tearDown() return def eval_formatted_output(self, limit_line_comp=False): @@ -617,7 +621,7 @@ def test_format_ssj_boundary_files_mixed_input(self): """Test `format_ssj_boundary_files` with mixed good/bad file input.""" # Create an empty, badly formatted input file - self.csv_files.append(os.path.join(self.test_dir, "".join([ + self.csv_files.append(os.path.join(cc.test_dir, "".join([ "dmsp-f47_ssj_precipitating-electrons-ion_20101231_v1.1.2_", "boundaries.csv"]))) with open(self.csv_files[-1], "w") as fp: @@ -634,8 +638,7 @@ def test_format_ssj_boundary_files_mixed_input(self): self.eval_formatted_output() # Evaluate the logging messages - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() # Prepare the empty file for clean up self.out.append(self.csv_files[-1]) @@ -664,11 +667,11 @@ def test_format_ssj_boundary_files_notafile_failure(self): with self.assertRaisesRegex(ValueError, "empty list of input CSV"): # Try to read in a bad CDF filename - self.out = dmsp_ssj_files.format_ssj_boundary_files([self.test_dir]) + self.out = dmsp_ssj_files.format_ssj_boundary_files([cc.test_dir]) # Test the logging output - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, "bad input file") + self.lwarn = "bad input file" + self.eval_logging_message() return def test_format_ssj_boundary_files_failure(self): @@ -686,20 +689,15 @@ class TestSSJFetchFormat(unittest.TestCase): def setUp(self): """Initialize the test class.""" - self.ocb_dir = os.path.dirname(ocbpy.__file__) - self.test_dir = os.path.join(self.ocb_dir, "tests") + self.test_dir = os.path.split(cc.test_dir)[0] self.comp_files = {"dmsp-ssj_north_20101231_20101231_v1.1.2.eab": - os.path.join(self.test_dir, "test_data", - "dmsp-ssj_north_out.eab"), + os.path.join(cc.test_dir, "dmsp-ssj_north_out.eab"), "dmsp-ssj_south_20101231_20101231_v1.1.2.eab": - os.path.join(self.test_dir, "test_data", - "dmsp-ssj_south_out.eab"), + os.path.join(cc.test_dir, "dmsp-ssj_south_out.eab"), "dmsp-ssj_north_20101231_20101231_v1.1.2.ocb": - os.path.join(self.test_dir, "test_data", - "dmsp-ssj_north_out.ocb"), + os.path.join(cc.test_dir, "dmsp-ssj_north_out.ocb"), "dmsp-ssj_south_20101231_20101231_v1.1.2.ocb": - os.path.join(self.test_dir, "test_data", - "dmsp-ssj_south_out.ocb")} + os.path.join(cc.test_dir, "dmsp-ssj_south_out.ocb")} self.in_args = [dt.datetime(2010, 12, 31), dt.datetime(2011, 1, 1), self.test_dir] self.out = list() @@ -713,8 +711,7 @@ def tearDown(self): for ff in self.out: os.remove(ff) - del self.ocb_dir, self.out, self.test_dir, self.in_args - del self.comp_files, self.ldtype + del self.out, self.in_args, self.comp_files, self.ldtype, self.test_dir return def test_use_deprecated(self): From 14e4e67079184b3975e65564381ebe9ffb61c914 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 12:15:55 -0400 Subject: [PATCH 26/73] BUG: fixed file cleanup Fixed file clean up for a test and re-combined logging tests after debugging. --- ocbpy/tests/test_dmsp_ssj_files.py | 37 +++++++++++------------------- 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/ocbpy/tests/test_dmsp_ssj_files.py b/ocbpy/tests/test_dmsp_ssj_files.py index fc7b2296..48bf9efd 100644 --- a/ocbpy/tests/test_dmsp_ssj_files.py +++ b/ocbpy/tests/test_dmsp_ssj_files.py @@ -243,8 +243,8 @@ def test_deprecated_function(self): dep_str = "ssj_auroral_boundaries package is no longer supported" with self.assertWarnsRegex(DeprecationWarning, dep_str): - dmsp_ssj_files.create_ssj_boundary_files(self.cdf_files, - out_dir=self.test_dir) + self.out = dmsp_ssj_files.create_ssj_boundary_files( + self.cdf_files, out_dir=self.test_dir) return def test_create_ssj_boundary_files_failure(self): @@ -268,30 +268,19 @@ def test_create_ssj_boundary_files_outcols_failure(self): self.cdf_files, out_dir=self.test_dir, out_cols=['fake']) return - def test_create_ssj_boundary_files_log_cdf_failure(self): - """Test create_ssj_boundary_files raising CDF logging warning.""" - - # Run with bad input file - for fname in self.comp_files: - self.assertTrue(os.path.isfile(fname), - msg="missing test file: {:}".format(fname)) - self.out = dmsp_ssj_files.create_ssj_boundary_files(self.comp_files) - self.assertEqual(len(self.out), 0) + def test_create_ssj_boundary_files_log_failure(self): + """Test create_ssj_boundary_files raising logging warning.""" - # Test logging output - self.lwarn = "CDF" - self.eval_logging_message() - return + # Run with bad input files + for in_files, self.lwarn in ([self.comp_files, "CDF"], + [[cc.test_dir], "bad input file"]): + with self.subTest(in_files=in_files): + self.out = dmsp_ssj_files.create_ssj_boundary_files(in_files) + self.assertEqual(len(self.out), 0, + msg="unexpected output: {:}".format(self.out)) - def test_create_ssj_boundary_files_log_not_a_file_failure(self): - """Test create_ssj_boundary_files raising bad file logging warning.""" - # Run with bad input file - self.out = dmsp_ssj_files.create_ssj_boundary_files([cc.test_dir]) - self.assertEqual(len(self.out), 0) - - # Test logging output - self.lwarn = "bad input file" - self.eval_logging_message() + # Test logging output + self.eval_logging_message() return def test_create_ssj_boundary_files_default(self): From 30dedee97613d57abf0c2e30e5675e3f98771678 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 12:16:29 -0400 Subject: [PATCH 27/73] STY: removed unused import Removed an unused import. --- ocbpy/tests/test_dmsp_ssj_files.py | 1 - 1 file changed, 1 deletion(-) diff --git a/ocbpy/tests/test_dmsp_ssj_files.py b/ocbpy/tests/test_dmsp_ssj_files.py index 48bf9efd..ffd44205 100644 --- a/ocbpy/tests/test_dmsp_ssj_files.py +++ b/ocbpy/tests/test_dmsp_ssj_files.py @@ -13,7 +13,6 @@ from requests.exceptions import ProxyError, ConnectionError -import ocbpy from ocbpy import boundaries import ocbpy.tests.class_common as cc From 0b2acf1c43c8201339f969fc4fed418f97d95e63 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 12:37:41 -0400 Subject: [PATCH 28/73] ENH: improved fetch robustness Ensure archive filename can be found whether or not it was freshly downloaded to the desired location. --- ocbpy/boundaries/dmsp_ssj_files.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/ocbpy/boundaries/dmsp_ssj_files.py b/ocbpy/boundaries/dmsp_ssj_files.py index 7f38aea9..c823de87 100644 --- a/ocbpy/boundaries/dmsp_ssj_files.py +++ b/ocbpy/boundaries/dmsp_ssj_files.py @@ -324,13 +324,21 @@ def fetch_ssj_boundary_files(stime=None, etime=None, out_dir=None, # Get the archive name from the output try: - zind = zen_split.index('Link:') + 1 - archive_name = os.path.join(out_dir, os.path.split(zen_split[zind])[-1]) + link_ind = zen_split.index('Link:') + 1 + + # If the archive is already available, message may differ + zip_name = os.path.split(zen_split[link_ind]) + while zip_name[-1].find(".zip") <= 0: + zip_name = os.path.split(zip_name[0]) + + # Set the archive name + archive_name = os.path.join(out_dir, zip_name[-1]) except (ValueError, IndexError): raise IOError('unable to identify zenodo archive: {:}'.format(zen_msg)) if not os.path.isfile(archive_name): - raise IOError('error downloading archive to output dir') + raise IOError('error downloading archive to output dir: {:}'.format( + archive_name)) # Access the zip archive with zipfile.ZipFile(archive_name, 'r') as zref: From a1c4513b1a138a144bf155bee4b4e24674a5318b Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 13:02:29 -0400 Subject: [PATCH 29/73] STY: use common test elements in cycle tests Update the cycle unit tests to use the common test class and variable. --- ocbpy/tests/test_cycle_boundary.py | 102 ++++++++++------------------- 1 file changed, 33 insertions(+), 69 deletions(-) diff --git a/ocbpy/tests/test_cycle_boundary.py b/ocbpy/tests/test_cycle_boundary.py index 3f670782..cb258a3a 100644 --- a/ocbpy/tests/test_cycle_boundary.py +++ b/ocbpy/tests/test_cycle_boundary.py @@ -6,25 +6,23 @@ """Test the cycle_boundary sub-module functions.""" import datetime as dt -from io import StringIO import logging import numpy as np from os import path import unittest import ocbpy +import ocbpy.tests.class_common as cc -class TestCycleMatchData(unittest.TestCase): +class TestCycleMatchData(cc.TestLogWarnings): """Unit tests for the `match_data_ocb` function.""" def setUp(self): """Initialize the test environment.""" - set_north = {"filename": path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", - "test_north_circle"), - "instrument": "image"} - self.ocb = ocbpy.OCBoundary(**set_north) + self.ocb = ocbpy.OCBoundary(filename=path.join(cc.test_dir, + "test_north_circle"), + instrument="image", hemisphere=1) self.ocb.rec_ind = -1 self.idat = 0 self.test_func = ocbpy.cycle_boundary.match_data_ocb @@ -35,19 +33,14 @@ def setUp(self): seconds=self.del_time + 1) # Initialize logging - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - del set_north + super().setUp() return def tearDown(self): """Clean up the test environment.""" - del self.ocb, self.lwarn, self.lout, self.log_capture, self.idat - del self.test_func, self.rec_ind, self.rec_ind2, self.del_time - del self.bad_time + del self.ocb, self.idat, self.bad_time, self.del_time + del self.test_func, self.rec_ind, self.rec_ind2 + super().tearDown() return def test_bad_class_cycling_method(self): @@ -123,13 +116,14 @@ def test_good_first_match(self): # Because the array starts at the first good OCB, will return zero self.idat = self.test_func(self.ocb, [self.ocb.dtime[self.rec_ind]], idat=self.idat) + + # Test the output self.assertEqual(self.idat, 0) self.assertEqual(self.ocb.rec_ind, self.rec_ind) # The first match will be announced in the log self.lwarn = "found first good OCB record at" - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return def test_bad_first_match(self): @@ -143,8 +137,7 @@ def test_bad_first_match(self): # The first match will be announced in the log self.lwarn = "unable to find a good OCB record" - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return def test_bad_ocb_ind(self): @@ -191,8 +184,7 @@ def test_data_all_before_first_ocb_record(self): # Check the log output self.lwarn = "no input data close enough to the first record" - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return def test_late_data_time_alignment(self): @@ -209,8 +201,8 @@ def test_late_data_time_alignment(self): # Check the log output self.lwarn = "no OCB data available within" - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() + self.lwarn = "of first measurement" self.assertRegex(self.lout, self.lwarn) return @@ -228,8 +220,8 @@ def test_no_data_time_alignment(self): # Check the log output self.lwarn = "no OCB data available within" - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() + self.lwarn = "of input measurement" self.assertRegex(self.lout, self.lwarn) return @@ -240,38 +232,17 @@ class TestCycleMatchDualData(TestCycleMatchData): def setUp(self): """Initialize the test environment.""" - set_dual = {"ocb_filename": path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", - "test_north_circle"), - "ocb_instrument": "image", 'eab_instrument': 'image', - 'eab_filename': path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", - "test_north_eab")} - self.ocb = ocbpy.DualBoundary(**set_dual) + # Initalize the default test environment + super().setUp() + + # Alter the test environment + self.ocb = ocbpy.DualBoundary( + ocb_filename=path.join(cc.test_dir, "test_north_circle"), + ocb_instrument="image", eab_instrument='image', hemisphere=1, + eab_filename=path.join(cc.test_dir, "test_north_eab")) self.ocb.rec_ind = -1 - self.idat = 0 - self.test_func = ocbpy.cycle_boundary.match_data_ocb self.rec_ind = 0 self.rec_ind2 = 1 - self.del_time = 60 - self.bad_time = self.ocb.ocb.dtime[37] - dt.timedelta( - seconds=self.del_time + 1) - - # Initialize logging - self.lwarn = u"" - self.lout = u"" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - del set_dual - - return - - def tearDown(self): - """Clean up the test environment.""" - del self.ocb, self.lwarn, self.lout, self.log_capture, self.idat - del self.test_func, self.rec_ind, self.rec_ind2, self.del_time - del self.bad_time return @@ -280,17 +251,13 @@ class TestCycleGoodIndices(unittest.TestCase): def setUp(self): """Initialize the test environment.""" - set_north = {"filename": path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", - "test_north_circle"), - "instrument": "image"} - self.ocb = ocbpy.OCBoundary(**set_north) + self.ocb = ocbpy.OCBoundary( + filename=path.join(cc.test_dir, "test_north_circle"), + instrument="image", hemisphere=1) self.ocb.rec_ind = -1 self.test_func = ocbpy.cycle_boundary.retrieve_all_good_indices self.rec_ind = 27 self.rec_ind2 = 31 - - del set_north return def tearDown(self): @@ -332,12 +299,9 @@ class TestGeneralSatelliteFunctions(unittest.TestCase): def setUp(self): """Set up the test environment.""" - self.test_dir = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data") - self.ocb = ocbpy.OCBoundary(instrument="dmsp-ssj", hemisphere=1, - filename=path.join( - self.test_dir, - "dmsp-ssj_north_out.ocb")) + self.ocb = ocbpy.OCBoundary( + instrument="dmsp-ssj", hemisphere=1, + filename=path.join(cc.test_dir, "dmsp-ssj_north_out.ocb")) self.mlt = np.arange(0, 24, 0.5) self.lat = np.full(shape=self.mlt.shape, fill_value=75.0) @@ -351,7 +315,7 @@ def setUp(self): def tearDown(self): """Clean up the test environment.""" - del self.test_dir, self.ocb, self.mlt, self.lat, self.good + del self.ocb, self.mlt, self.lat, self.good return def test_satellite_track_defaults(self): From a8020e0db43a6cd6fed188ed3f0ef8e7ffc47d95 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 14:53:14 -0400 Subject: [PATCH 30/73] STY: use common class in boundary tests Updated the boundary tests to use the common classes and variables. Also updated behaviour to remove numpy deprecation warnings. --- ocbpy/tests/test_boundary_dual.py | 115 ++++++----------- ocbpy/tests/test_boundary_eab.py | 150 +++++++--------------- ocbpy/tests/test_boundary_ocb.py | 200 +++++++++++++++--------------- 3 files changed, 186 insertions(+), 279 deletions(-) diff --git a/ocbpy/tests/test_boundary_dual.py b/ocbpy/tests/test_boundary_dual.py index 79cf70aa..b0ccb4e9 100644 --- a/ocbpy/tests/test_boundary_dual.py +++ b/ocbpy/tests/test_boundary_dual.py @@ -5,36 +5,21 @@ # ----------------------------------------------------------------------------- """Tests the boundary DualBoundary class.""" -from io import StringIO -import logging import numpy from os import path import sys import unittest import ocbpy -from . import test_boundary_ocb as test_ocb +import ocbpy.tests.class_common as cc +import ocbpy.tests.test_boundary_ocb as test_ocb win_list = ['windows', 'win32', 'win64', 'cygwin'] -class TestDualBoundaryLogFailure(unittest.TestCase): +class TestDualBoundaryLogFailure(cc.TestLogWarnings): """Test the logging messages raised by the DualBoundary class.""" - def setUp(self): - """Initialize the test class.""" - self.lwarn = "" - self.lout = "" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - return - - def tearDown(self): - """Tear down the test case.""" - del self.lwarn, self.lout, self.log_capture - return - def test_bad_instrument_name(self): """Test OCB initialization with bad instrument name.""" self.lwarn = "OCB instrument must be a string" @@ -53,15 +38,13 @@ def test_bad_instrument_name(self): self.assertIsNone(subclass.filename) self.assertIsNone(subclass.instrument) - self.lout = self.log_capture.getvalue() - # Test logging error message for each bad initialization - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return def test_bad_filename(self): """Test initialization with a bad default file/instrument pairing.""" - self.lwarn = "name provided is not a file\ncannot open OCB file [hi]" + self.lwarn = "name provided is not a file\ncannot open OCB file " # Try to load data with a non-existant file name for btype in ["eab", "ocb"]: @@ -75,14 +58,8 @@ def test_bad_filename(self): # Test the values for the sub-class self.assertIsNone(subclass.filename) - self.lout = self.log_capture.getvalue() - # Test logging error message for each bad initialization - self.assertTrue( - self.lout.find(self.lwarn) >= 0, - msg="logging output {:} != expected output {:}".format( - repr(self.lout), repr(self.lwarn))) - + self.eval_logging_message() return @@ -91,9 +68,10 @@ class TestDualBoundaryInstruments(test_ocb.TestOCBoundaryInstruments): def setUp(self): """Initialize the instrument information.""" + super().setUp() + + # Update the test attributes self.test_class = ocbpy.DualBoundary - self.test_dir = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data") self.inst_attrs = {"image": ["year", "soy", "num_sectors", "a", "r_err", "fom"], "ampere": ["date", "time", "x", "y", "fom"], @@ -107,30 +85,23 @@ def setUp(self): "dmsp-ssj": ["year", "soy", "num_sectors", "a", "r_err"]} self.inst_init = [{"eab_instrument": "dmsp-ssj", "hemisphere": 1, - "eab_filename": path.join(self.test_dir, + "eab_filename": path.join(cc.test_dir, "dmsp-ssj_north_out.eab"), "ocb_instrument": "image", - "ocb_filename": path.join(self.test_dir, + "ocb_filename": path.join(cc.test_dir, "test_north_circle")}, {"eab_instrument": "dmsp-ssj", "hemisphere": 1, - "eab_filename": path.join(self.test_dir, + "eab_filename": path.join(cc.test_dir, "dmsp-ssj_north_out.eab"), "ocb_instrument": "dmsp-ssj", - "ocb_filename": path.join(self.test_dir, + "ocb_filename": path.join(cc.test_dir, "dmsp-ssj_north_out.ocb")}, {"eab_instrument": "dmsp-ssj", "hemisphere": -1, - "eab_filename": path.join(self.test_dir, + "eab_filename": path.join(cc.test_dir, "dmsp-ssj_south_out.eab"), "ocb_instrument": "ampere", - "ocb_filename": path.join(self.test_dir, + "ocb_filename": path.join(cc.test_dir, "test_south_circle")}] - self.ocb = None - return - - def tearDown(self): - """Clean up the test environment.""" - del self.test_dir, self.inst_attrs, self.inst_init, self.ocb - del self.test_class return @@ -139,25 +110,21 @@ class TestDualBoundaryMethodsGeneral(test_ocb.TestOCBoundaryMethodsGeneral): def setUp(self): """Initialize the test environment.""" + super().setUp() + + # Update the class attributes self.test_class = ocbpy.DualBoundary - test_dir = path.join(path.dirname(ocbpy.__file__), "tests", "test_data") - self.set_empty = {"ocb_filename": path.join(test_dir, "test_empty"), - "eab_filename": path.join(test_dir, "test_empty"), + self.set_empty = {"ocb_filename": path.join(cc.test_dir, "test_empty"), + "eab_filename": path.join(cc.test_dir, "test_empty"), "ocb_instrument": "image", "eab_instrument": "image", "hemisphere": 1} self.set_default = {"ocb_filename": - path.join(test_dir, "dmsp-ssj_north_out.ocb"), + path.join(cc.test_dir, "dmsp-ssj_north_out.ocb"), "eab_filename": - path.join(test_dir, "dmsp-ssj_north_out.eab"), + path.join(cc.test_dir, "dmsp-ssj_north_out.eab"), "ocb_instrument": "dmsp-ssj", "eab_instrument": "dmsp-ssj", "hemisphere": 1, "max_delta": 600} - self.ocb = None - return - - def tearDown(self): - """Clean up the test environment.""" - del self.set_empty, self.set_default, self.ocb return def test_repr_string(self): @@ -355,13 +322,15 @@ def test_custom_ind_selection(self): return -class TestDualBoundaryMethodsLocation(unittest.TestCase): +class TestDualBoundaryMethodsLocation(cc.TestLogWarnings): """Test the DualBoundary location methods.""" def setUp(self): """Initialize the test environment.""" - self.test_dir = path.join(path.dirname(ocbpy.__file__), - "tests", "test_data") + # Set the logging attributes + super().setUp() + + # Set the local attributes self.set_default = {"ocb_instrument": "dmsp-ssj", "eab_instrument": "dmsp-ssj", "max_delta": 60} @@ -407,21 +376,14 @@ def setUp(self): -1: [15.785, 15.785, 5.10033853, 5.12896403, 66.00490331, 68.25074784, 68.91414059]} self.out = [] - - # Set the logging parameters - self.lwarn = "" - self.lout = "" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - return def tearDown(self): """Clean up the test environment.""" - del self.test_dir, self.set_default, self.dual, self.mlt, self.lat - del self.out, self.bounds, self.lwarn, self.lout, self.log_capture - del self.bad_mag, self.bad_norm + super().tearDown() + + del self.set_default, self.dual, self.mlt, self.lat + del self.out, self.bounds, self.bad_mag, self.bad_norm return def update_default_kwargs(self, hemisphere=1): @@ -450,7 +412,7 @@ def update_default_kwargs(self, hemisphere=1): fkey = '_'.join([bname, 'filename']) if self.set_default[ikey] == 'dmsp-ssj': self.set_default[fkey] = path.join( - self.test_dir, 'dmsp-ssj_{:s}_out.{:s}'.format( + cc.test_dir, 'dmsp-ssj_{:s}_out.{:s}'.format( hemi_name[hemisphere], bname)) return @@ -650,12 +612,11 @@ def test_poorly_defined_boundary_normal_coord(self): # Get the output self.out = self.dual.normal_coord(*self.bad_mag) - self.lout = self.log_capture.getvalue() # Evaluate the output + self.eval_logging_message() self.assertTrue(numpy.isnan(self.out[0])) self.assertAlmostEqual(self.out[1], self.bad_norm[1]) - self.assertRegex(self.lout, self.lwarn) return @@ -672,18 +633,16 @@ def test_poorly_defined_boundary_revert_coord(self): # Get the output self.out = self.dual.revert_coord(*self.bad_norm, is_ocb=False, aacgm_mlt=self.bad_mag[1]) - self.lout = self.log_capture.getvalue() # Evaluate the output + self.eval_logging_message() self.assertTrue(numpy.isnan(self.out[0])) self.assertAlmostEqual(self.out[1], self.bad_mag[1]) - self.assertRegex(self.lout, self.lwarn) return def test_coord_method_float_nan(self): """Test the coord method calculations with NaN float input.""" - ind = 0 self.rcorr = numpy.nan @@ -1110,15 +1069,13 @@ class TestDualBoundaryFailure(unittest.TestCase): def setUp(self): """Set up the test environment.""" - test_dir = path.join(path.dirname(ocbpy.__file__), - "tests", "test_data") self.set_default = {"ocb_instrument": "dmsp-ssj", "eab_instrument": "dmsp-ssj", "hemisphere": 1, "ocb_filename": path.join( - test_dir, 'dmsp-ssj_north_out.ocb'), + cc.test_dir, 'dmsp-ssj_north_out.ocb'), "eab_filename": path.join( - test_dir, 'dmsp-ssj_north_out.eab')} + cc.test_dir, 'dmsp-ssj_north_out.eab')} self.dual = None def tearDown(self): diff --git a/ocbpy/tests/test_boundary_eab.py b/ocbpy/tests/test_boundary_eab.py index aeeaea3f..76244d12 100644 --- a/ocbpy/tests/test_boundary_eab.py +++ b/ocbpy/tests/test_boundary_eab.py @@ -5,13 +5,12 @@ # ----------------------------------------------------------------------------- """Tests the boundary EABoundary class.""" -from io import StringIO -import logging import numpy from os import path import ocbpy -from . import test_boundary_ocb as test_ocb +import ocbpy.tests.class_common as cc +import ocbpy.tests.test_boundary_ocb as test_ocb class TestEABoundaryLogFailure(test_ocb.TestOCBoundaryLogFailure): @@ -19,23 +18,13 @@ class TestEABoundaryLogFailure(test_ocb.TestOCBoundaryLogFailure): def setUp(self): """Initialize the test class.""" + super().setUp() + + # Update the setup self.test_class = ocbpy.EABoundary - test_dir = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data") self.inst_init = {"instrument": "image", "hemisphere": 1, - "filename": path.join(test_dir, + "filename": path.join(cc.test_dir, "test_north_circle")} - - self.lwarn = "" - self.lout = "" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - return - - def tearDown(self): - """Tear down the test case.""" - del self.lwarn, self.lout, self.log_capture, self.test_class return @@ -44,9 +33,10 @@ class TestEABoundaryInstruments(test_ocb.TestOCBoundaryInstruments): def setUp(self): """Initialize the instrument information.""" + super().setUp() + + # Update the attributes self.test_class = ocbpy.EABoundary - self.test_dir = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data") self.inst_attrs = {"image": ["year", "soy", "num_sectors", "a", "r_err", "fom"], "dmsp-ssj": ["date", "time", "sc", "x", "y", "fom", @@ -56,21 +46,14 @@ def setUp(self): "dmsp-ssj": ["year", "soy", "num_sectors", "a", "r_err"]} self.inst_init = [{"instrument": "image", "hemisphere": 1, - "filename": path.join(self.test_dir, + "filename": path.join(cc.test_dir, "test_north_circle")}, {"instrument": "dmsp-ssj", "hemisphere": 1, - "filename": path.join(self.test_dir, + "filename": path.join(cc.test_dir, "dmsp-ssj_north_out.eab")}, {"instrument": "dmsp-ssj", "hemisphere": -1, - "filename": path.join(self.test_dir, + "filename": path.join(cc.test_dir, "dmsp-ssj_south_out.eab")}] - self.ocb = None - return - - def tearDown(self): - """Clean up the test environment.""" - del self.test_dir, self.inst_attrs, self.inst_init, self.ocb - del self.test_class return @@ -79,23 +62,9 @@ class TestEABoundaryMethodsGeneral(test_ocb.TestOCBoundaryMethodsGeneral): def setUp(self): """Initialize the test environment.""" - self.test_class = ocbpy.EABoundary - ocb_dir = path.dirname(ocbpy.__file__) - self.set_empty = {"filename": path.join(ocb_dir, "tests", "test_data", - "test_empty"), - "instrument": "image"} - self.set_default = {"filename": path.join(ocb_dir, "tests", - "test_data", - "test_north_circle"), - "instrument": "image"} - self.assertTrue(path.isfile(self.set_empty['filename'])) - self.assertTrue(path.isfile(self.set_default['filename'])) - self.ocb = None - return + super().setUp() - def tearDown(self): - """Clean up the test environment.""" - del self.set_empty, self.set_default, self.ocb, self.test_class + self.test_class = ocbpy.EABoundary return @@ -104,65 +73,53 @@ class TestEABoundaryMethodsNorth(test_ocb.TestOCBoundaryMethodsNorth): def setUp(self): """Initialize the test environment.""" + super().setUp() + self.test_class = ocbpy.EABoundary self.ref_boundary = 64.0 - self.set_north = {'filename': path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", - "test_north_circle"), - 'instrument': 'image'} - self.assertTrue(path.isfile(self.set_north['filename'])) - self.ocb = self.test_class(**self.set_north) - self.ocb.rec_ind = 27 - - self.mlt = numpy.linspace(0.0, 24.0, num=6) - self.lat = numpy.linspace(0.0, 90.0, num=len(self.mlt)) + self.ocb_lat = [numpy.nan, -37.95918548, -6.92874899, 20.28409774, 51.9732, 84.90702626] self.ocb_mlt = [numpy.nan, 4.75942194, 9.76745427, 14.61843964, 19.02060793, 17.832] - self.r_corr = 0.0 - self.out = None - return - - def tearDown(self): - """Clean up the test environment.""" - del self.ocb, self.set_north, self.mlt, self.lat, self.ocb_lat - del self.ocb_mlt, self.r_corr, self.out, self.test_class - del self.ref_boundary return def test_normal_coord_north_geodetic(self): """Test the geodetic normalisation calculation in the north.""" + self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1], coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), 72.5526, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.3839, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], 72.5526, places=3) + self.assertAlmostEqual(self.out[1][0], 19.3839, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_north_geocentric(self): """Test the geocentric normalisation calculation in the north.""" + self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1], coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), 72.5564, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.3852, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], 72.5564, places=3) + self.assertAlmostEqual(self.out[1][0], 19.3852, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_revert_coord_north_geodetic(self): """Test the reversion to geodetic coordinates in the north.""" + self.set_ocb() self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], self.r_corr, coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), 77.13321838, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.18124285, places=3) + self.assertAlmostEqual(self.out[0][0], 77.13321838, places=3) + self.assertAlmostEqual(self.out[1][0], 19.18124285, places=3) return def test_revert_coord_north_geocentric(self): """Test the reversion to geocentric coordinates in the north.""" + self.set_ocb() self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], self.r_corr, coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), 77.05394766, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.18124285, places=3) + self.assertAlmostEqual(self.out[0][0], 77.05394766, places=3) + self.assertAlmostEqual(self.out[1][0], 19.18124285, places=3) return @@ -171,10 +128,11 @@ class TestEABoundaryMethodsSouth(test_ocb.TestOCBoundaryMethodsSouth): def setUp(self): """Initialize the test environment.""" + super().setUp() + self.test_class = ocbpy.EABoundary self.ref_boundary = -64.0 - self.set_south = {"filename": path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", + self.set_south = {"filename": path.join(cc.test_dir, "dmsp-ssj_south_out.eab"), "instrument": "dmsp-ssj", "hemisphere": -1, @@ -187,15 +145,6 @@ def setUp(self): self.lat = numpy.full(shape=self.mlt.shape, fill_value=-75.0) self.ocb_lat = [-84.51747815, -82.89664467, -76.7993294, -78.4535674] self.ocb_mlt = [6.02245268, 6.69193021, 18.56617597, 18.88837759] - self.r_corr = 0.0 - self.out = None - return - - def tearDown(self): - """Clean up the test environment.""" - del self.ocb, self.set_south, self.mlt, self.lat, self.ocb_lat - del self.ocb_mlt, self.r_corr, self.out, self.test_class - del self.ref_boundary return def test_dmspssj_attrs(self): @@ -220,9 +169,9 @@ def test_normal_coord_south_geocentric(self): self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0], height=830, coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), -76.3801206, places=3) - self.assertAlmostEqual(float(self.out[1]), 20.494021798, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], -76.3801206, places=3) + self.assertAlmostEqual(self.out[1][0], 20.494021798, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_south_geodetic(self): @@ -230,25 +179,25 @@ def test_normal_coord_south_geodetic(self): self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0], height=830, coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), -76.34575670, places=3) - self.assertAlmostEqual(float(self.out[1]), 20.524524159, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], -76.34575670, places=3) + self.assertAlmostEqual(self.out[1][0], 20.524524159, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_revert_coord_south_geodetic(self): """Test the reversion to geodetic coordinates in the south.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr, coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), -66.317542, places=3) - self.assertAlmostEqual(float(self.out[1]), 5.90843131, places=3) + self.assertAlmostEqual(self.out[0][0], -66.317542, places=3) + self.assertAlmostEqual(self.out[1][0], 5.90843131, places=3) return def test_revert_coord_south_geocentric(self): """Test the reversion to geocentric coordinates in the south.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr, coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), -66.1832772, places=3) - self.assertAlmostEqual(float(self.out[1]), 5.90843131, places=3) + self.assertAlmostEqual(self.out[0][0], -66.1832772, places=3) + self.assertAlmostEqual(self.out[1][0], 5.90843131, places=3) return def test_normal_coord_south_corrected(self): @@ -257,9 +206,9 @@ def test_normal_coord_south_corrected(self): self.ocb.rfunc_kwargs[self.ocb.rec_ind]['r_add'] = self.r_corr self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0]) - self.assertAlmostEqual(float(self.out[0]), -84.764005494, places=3) - self.assertAlmostEqual(float(self.out[1]), 6.02245269240, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], -84.764005494, places=3) + self.assertAlmostEqual(self.out[1][0], 6.02245269240, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_aacgm_boundary_location_good_south(self): @@ -382,8 +331,3 @@ def setUp(self): """Set up the test environment.""" self.test_class = ocbpy.EABoundary return - - def tearDown(self): - """Clean up the test environment.""" - del self.test_class - return diff --git a/ocbpy/tests/test_boundary_ocb.py b/ocbpy/tests/test_boundary_ocb.py index aedb5554..bb688023 100644 --- a/ocbpy/tests/test_boundary_ocb.py +++ b/ocbpy/tests/test_boundary_ocb.py @@ -6,38 +6,31 @@ """Tests the boundary OCBoundary class.""" import datetime -from io import StringIO -import logging import numpy from os import path import unittest import ocbpy +import ocbpy.tests.class_common as cc -class TestOCBoundaryLogFailure(unittest.TestCase): +class TestOCBoundaryLogFailure(cc.TestLogWarnings): """Test the logging messages raised by the OCBoundary class.""" def setUp(self): """Initialize the test class.""" + super().setUp() self.test_class = ocbpy.OCBoundary - test_dir = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data") self.inst_init = {"instrument": "image", "hemisphere": 1, - "filename": path.join(test_dir, + "filename": path.join(cc.test_dir, "test_north_circle")} - self.lwarn = "" - self.lout = "" - self.log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(self.log_capture)) - ocbpy.logger.setLevel(logging.WARNING) return def tearDown(self): """Tear down the test case.""" - del self.lwarn, self.lout, self.log_capture, self.test_class - del self.inst_init + super().tearDown() + del self.test_class, self.inst_init return def test_bad_get_next_good_ocb_ind_kwargs(self): @@ -52,8 +45,7 @@ def test_bad_get_next_good_ocb_ind_kwargs(self): bound.get_next_good_ocb_ind(**{"NotAnAttr": ("max", 5.0)}) # Get and test the log message - self.lout = self.log_capture.getvalue() - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return def test_bad_instrument_name(self): @@ -67,9 +59,8 @@ def test_bad_instrument_name(self): self.assertIsNone(ocb.filename) self.assertIsNone(ocb.instrument) - self.lout = self.log_capture.getvalue() # Test logging error message for each bad initialization - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return @@ -83,25 +74,20 @@ def test_bad_file_name(self): ocb = self.test_class(filename=val) self.assertIsNone(ocb.filename) - self.lout = self.log_capture.getvalue() # Test logging error message for each bad initialization - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return def test_bad_filename(self): """Test initialization with a bad default file/instrument pairing.""" - self.lwarn = "name provided is not a file\ncannot open OCB file [hi]" + self.lwarn = "name provided is not a file\ncannot open OCB file " # Try to load data with a non-existant file name ocb = self.test_class(filename="hi") self.assertIsNone(ocb.filename) - self.lout = self.log_capture.getvalue() - # Test logging error message for each bad initialization - self.assertTrue(self.lout.find(self.lwarn) >= 0, - msg="logging output {:} != expected output {:}".format( - repr(self.lout), repr(self.lwarn))) + self.eval_logging_message() return @@ -121,9 +107,8 @@ def test_bad_time_structure(self): # Load the data, skipping the year ocb.load(ocb_cols="skip soy num_sectors phi_cent r_cent r a r_err") - self.lout = self.log_capture.getvalue() # Test logging error message for the non-None bad initializations - self.assertRegex(self.lout, self.lwarn) + self.eval_logging_message() return @@ -134,8 +119,6 @@ class TestOCBoundaryInstruments(unittest.TestCase): def setUp(self): """Initialize the instrument information.""" self.test_class = ocbpy.OCBoundary - self.test_dir = path.join(path.dirname(ocbpy.__file__), "tests", - "test_data") self.inst_attrs = {"image": ["year", "soy", "num_sectors", "a", "r_err", "fom"], "ampere": ["date", "time", "x", "y", "fom"], @@ -149,24 +132,23 @@ def setUp(self): "dmsp-ssj": ["year", "soy", "num_sectors", "a", "r_err"]} self.inst_init = [{"instrument": "image", "hemisphere": 1, - "filename": path.join(self.test_dir, + "filename": path.join(cc.test_dir, "test_north_circle")}, {"instrument": "dmsp-ssj", "hemisphere": 1, - "filename": path.join(self.test_dir, + "filename": path.join(cc.test_dir, "dmsp-ssj_north_out.ocb")}, {"instrument": "dmsp-ssj", "hemisphere": -1, - "filename": path.join(self.test_dir, + "filename": path.join(cc.test_dir, "dmsp-ssj_south_out.ocb")}, {"instrument": "ampere", "hemisphere": -1, - "filename": path.join(self.test_dir, + "filename": path.join(cc.test_dir, "test_south_circle")}] self.ocb = None return def tearDown(self): """Clean up the test environment.""" - del self.test_dir, self.inst_attrs, self.inst_init, self.ocb - del self.test_class + del self.inst_attrs, self.inst_init, self.ocb, self.test_class return def test_instrument_loading(self): @@ -206,16 +188,11 @@ class TestOCBoundaryMethodsGeneral(unittest.TestCase): def setUp(self): """Initialize the test environment.""" self.test_class = ocbpy.OCBoundary - ocb_dir = path.dirname(ocbpy.__file__) - self.set_empty = {"filename": path.join(ocb_dir, "tests", "test_data", - "test_empty"), + self.set_empty = {"filename": path.join(cc.test_dir, "test_empty"), "instrument": "image"} - self.set_default = {"filename": path.join(ocb_dir, "tests", - "test_data", + self.set_default = {"filename": path.join(cc.test_dir, "test_north_circle"), "instrument": "image"} - self.assertTrue(path.isfile(self.set_empty['filename'])) - self.assertTrue(path.isfile(self.set_default['filename'])) self.ocb = None return @@ -359,20 +336,18 @@ def test_custom_ind_selection(self): return -class TestOCBoundaryMethodsNorth(unittest.TestCase): +class TestOCBoundaryMethodsNorth(cc.TestLogWarnings): """Unit tests for the OCBoundary class in the northern hemisphere.""" def setUp(self): """Initialize the test environment.""" + super().setUp() + self.test_class = ocbpy.OCBoundary self.ref_boundary = 74.0 - self.set_north = {'filename': path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", + self.set_north = {'filename': path.join(cc.test_dir, "test_north_circle"), 'instrument': 'image'} - self.assertTrue(path.isfile(self.set_north['filename'])) - self.ocb = self.test_class(**self.set_north) - self.ocb.rec_ind = 27 self.mlt = numpy.linspace(0.0, 24.0, num=6) self.lat = numpy.linspace(0.0, 90.0, num=len(self.mlt)) @@ -382,6 +357,7 @@ def setUp(self): 19.02060793, 17.832] self.r_corr = 0.0 self.out = None + self.ocb = None return def tearDown(self): @@ -389,10 +365,20 @@ def tearDown(self): del self.ocb, self.set_north, self.mlt, self.lat, self.ocb_lat del self.ocb_mlt, self.r_corr, self.out, self.test_class del self.ref_boundary + + super().tearDown() + return + + def set_ocb(self): + """Set the OCB attribute.""" + self.assertTrue(path.isfile(self.set_north['filename'])) + self.ocb = self.test_class(**self.set_north) + self.ocb.rec_ind = 27 return def test_attrs(self): """Test the default attributes in the north.""" + self.set_ocb() for self.out in ["filename", "instrument", "hemisphere", "records", "rec_ind", "dtime", "phi_cent", "r_cent", "r", "boundary_lat", 'fom']: @@ -407,6 +393,7 @@ def test_attrs(self): def test_image_attrs(self): """Test IMAGE attributes in the north.""" + self.set_ocb() for self.out in ["num_sectors", "year", "soy", "r_err", "a", "fom"]: self.assertTrue(hasattr(self.ocb, self.out), msg="missing attr: {:s}".format(self.out)) @@ -414,6 +401,7 @@ def test_image_attrs(self): def test_ampere_attrs(self): """Test AMPERE attributes don't exist when IMAGE is loaded.""" + self.set_ocb() for self.out in ['date', 'time', 'x', 'y']: self.assertFalse(hasattr(self.ocb, self.out), msg="unexpected attr present: {:s}".format( @@ -422,6 +410,7 @@ def test_ampere_attrs(self): def test_dmspssj_attrs(self): """Test DMSP-SSJ attributes don't exist when IMAGE is loaded.""" + self.set_ocb() for self.out in ['sc', 'date', 'time', 'x', 'y', 'x_1', 'x_2', 'y_1', 'y_2']: @@ -432,12 +421,14 @@ def test_dmspssj_attrs(self): def test_load(self): """Ensure correctly loaded defaults in the north.""" + self.set_ocb() self.assertGreater(self.ocb.records, 0) self.assertEqual(self.ocb.boundary_lat, self.ref_boundary) return def test_partial_load(self): """Ensure limited sections of a file can be loaded in the north.""" + self.set_ocb() stime = self.ocb.dtime[0] + datetime.timedelta(seconds=1) etime = self.ocb.dtime[-1] - datetime.timedelta(seconds=1) @@ -454,14 +445,16 @@ def test_partial_load(self): def test_normal_coord_north_float(self): """Test the normalisation calculation in the north.""" + self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1]) - self.assertAlmostEqual(float(self.out[0]), self.ocb_lat[-1]) - self.assertAlmostEqual(float(self.out[1]), self.ocb_mlt[-1]) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], self.ocb_lat[-1]) + self.assertAlmostEqual(self.out[1][0], self.ocb_mlt[-1]) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_north_array(self): """Test normalisation calculation in the north with arryay input.""" + self.set_ocb() self.out = self.ocb.normal_coord(self.lat, self.mlt) self.assertTrue(numpy.all(numpy.less(abs(self.out[0] - self.ocb_lat), @@ -481,33 +474,37 @@ def test_normal_coord_north_array(self): def test_normal_coord_north_alt_mag_label(self): """Test normalisation calculation with good, but odd coord label.""" + self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1], coords='Mag') - self.assertAlmostEqual(float(self.out[0]), self.ocb_lat[-1]) - self.assertAlmostEqual(float(self.out[1]), self.ocb_mlt[-1]) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], self.ocb_lat[-1]) + self.assertAlmostEqual(self.out[1][0], self.ocb_mlt[-1]) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_north_geodetic(self): """Test the geodetic normalisation calculation in the north.""" + self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1], coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), 79.2631, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.3839, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], 79.2631, places=3) + self.assertAlmostEqual(self.out[1][0], 19.3839, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_north_geocentric(self): """Test the geocentric normalisation calculation in the north.""" + self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1], coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), 79.2654, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.3852, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], 79.2654, places=3) + self.assertAlmostEqual(self.out[1][0], 19.3852, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_north_w_south(self): """Test normalisation calculation in the north with southern lat.""" + self.set_ocb() self.out = self.ocb.normal_coord(-self.lat[-1], self.mlt[-1]) self.assertEqual(len(self.out), 3) @@ -516,6 +513,7 @@ def test_normal_coord_north_w_south(self): def test_normal_coord_low_rec_ind(self): """Test the normalization calculation failure with low record index.""" + self.set_ocb() self.ocb.rec_ind = -1 self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1]) @@ -525,6 +523,7 @@ def test_normal_coord_low_rec_ind(self): def test_normal_coord_high_rec_ind(self): """Test normalization calculation failure with high record index.""" + self.set_ocb() self.ocb.rec_ind = self.ocb.records + 1 self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1]) @@ -534,6 +533,7 @@ def test_normal_coord_high_rec_ind(self): def test_revert_coord_north_float(self): """Test the reversion to AACGM coordinates in the north.""" + self.set_ocb() self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], self.r_corr) self.assertAlmostEqual(self.out[0], self.lat[-2]) @@ -542,6 +542,7 @@ def test_revert_coord_north_float(self): def test_revert_coord_north_array(self): """Test reversion to AACGM coordinates in the north for an array.""" + self.set_ocb() self.out = self.ocb.revert_coord(self.ocb_lat, self.ocb_mlt, self.r_corr) @@ -563,6 +564,7 @@ def test_revert_coord_north_array(self): def test_revert_coord_north_coord_label(self): """Test reversion to AACGM coordinates in the north with Mag label.""" + self.set_ocb() self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], self.r_corr, coords='MAG') self.assertAlmostEqual(self.out[0], self.lat[-2]) @@ -571,22 +573,25 @@ def test_revert_coord_north_coord_label(self): def test_revert_coord_north_geodetic(self): """Test the reversion to geodetic coordinates in the north.""" + self.set_ocb() self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], self.r_corr, coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), 77.13321838, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.18124285, places=3) + self.assertAlmostEqual(self.out[0][0], 77.13321838, places=3) + self.assertAlmostEqual(self.out[1][0], 19.18124285, places=3) return def test_revert_coord_north_geocentric(self): """Test the reversion to geocentric coordinates in the north.""" + self.set_ocb() self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], self.r_corr, coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), 77.05394766, places=3) - self.assertAlmostEqual(float(self.out[1]), 19.18124285, places=3) + self.assertAlmostEqual(self.out[0][0], 77.05394766, places=3) + self.assertAlmostEqual(self.out[1][0], 19.18124285, places=3) return def test_revert_coord_north_w_south(self): """Test the reversion calculation in the north with southern lat.""" + self.set_ocb() self.out = self.ocb.revert_coord(-self.ocb_lat[-2], self.ocb_mlt[-2], self.r_corr) self.assertEqual(len(self.out), 2) @@ -594,8 +599,8 @@ def test_revert_coord_north_w_south(self): return def test_revert_coord_low_rec_ind(self): - """Test the reversion calculation failure with low record index - """ + """Test the reversion calculation failure with low record index.""" + self.set_ocb() self.ocb.rec_ind = -1 self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], @@ -606,6 +611,7 @@ def test_revert_coord_low_rec_ind(self): def test_revert_coord_high_rec_ind(self): """Test the reversion calculation failure with high record index.""" + self.set_ocb() self.ocb.rec_ind = self.ocb.records + 1 self.out = self.ocb.revert_coord(self.ocb_lat[-2], self.ocb_mlt[-2], @@ -616,25 +622,28 @@ def test_revert_coord_high_rec_ind(self): def test_default_boundary_input(self): """Test to see that the boundary latitude has the correct sign.""" + self.set_ocb() self.assertEqual(self.ocb.boundary_lat, self.ref_boundary) return def test_mismatched_boundary_input(self): """Test to see that the boundary latitude has the incorrect sign.""" self.set_north['hemisphere'] = -1 - self.out = self.test_class(**self.set_north) - self.assertEqual(self.out.boundary_lat, -self.ref_boundary) + self.set_ocb() + self.assertEqual(self.ocb.boundary_lat, -self.ref_boundary) return def test_mismatched_boundary_input_correction(self): """Test to see that the boundary latitude corrects the sign.""" self.set_north['boundary_lat'] = -70.0 - self.out = self.test_class(**self.set_north) - self.assertEqual(self.out.boundary_lat, 70.0) + self.set_ocb() + self.assertEqual(self.ocb.boundary_lat, 70.0) return def test_aacgm_boundary_location(self): """Test the calculation of the OCB in AACGM coordinates in the north.""" + self.set_ocb() + # Add new attributes self.ocb.get_aacgm_boundary_lat(self.mlt) @@ -663,6 +672,7 @@ def test_aacgm_boundary_location(self): def test_aacgm_boundary_location_good(self): """Test calculation of the OCB in AACGM coordinates in the north.""" + self.set_ocb() rind = 27 # Add the attribute at the good location @@ -680,6 +690,7 @@ def test_aacgm_boundary_location_good(self): def test_aacgm_boundary_location_bad(self): """Test calclation of the OCB in AACGM coordinates for limited MLTs.""" + self.set_ocb() rind = 2 # Add the attriubte at the bad location @@ -696,6 +707,7 @@ def test_aacgm_boundary_location_bad(self): def test_aacgm_boundary_location_no_input(self): """Test failure of OCB AACGM location calculation for no input.""" + self.set_ocb() with self.assertRaisesRegex(TypeError, "missing 1 required positional argument"): self.ocb.get_aacgm_boundary_lat() @@ -704,10 +716,6 @@ def test_aacgm_boundary_location_no_input(self): def test_aacgm_boundary_location_no_overwrite(self): """Ensure no overwrite when re-calculating OCB AACGM locations.""" - log_capture = StringIO() - ocbpy.logger.addHandler(logging.StreamHandler(log_capture)) - ocbpy.logger.setLevel(logging.WARNING) - # Initialize the attributes with values for the good location rind = 27 self.test_aacgm_boundary_location_good() @@ -718,16 +726,14 @@ def test_aacgm_boundary_location_no_overwrite(self): # This should raise a warning self.ocb.get_aacgm_boundary_lat(150.0, rec_ind=rind) - self.out = log_capture.getvalue() # Test logging error message for only one warning about boundary update - self.assertRegex(self.out, "unable to update AACGM boundary") + self.lwarn = "unable to update AACGM boundary" + self.eval_logging_message() - del log_capture return def test_aacgm_boundary_location_overwrite(self): """Test ability to overwrite OCB AACGM location.""" - # Initialize the attributes with values for the good location self.test_aacgm_boundary_location_good() @@ -743,6 +749,7 @@ def test_aacgm_boundary_location_overwrite(self): def test_aacgm_boundary_location_mlt_range(self): """Test failure of OCB AACGM location with different valued MLT.""" + self.set_ocb() self.mlt[self.mlt > 12.0] -= 24.0 self.ocb.get_aacgm_boundary_lat(self.mlt) @@ -758,8 +765,7 @@ def setUp(self): """Initialize the test environment.""" self.test_class = ocbpy.OCBoundary self.ref_boundary = -74.0 - self.set_south = {"filename": path.join(path.dirname(ocbpy.__file__), - "tests", "test_data", + self.set_south = {"filename": path.join(cc.test_dir, "test_south_circle"), "instrument": "ampere", "hemisphere": -1, @@ -863,9 +869,9 @@ def test_normal_coord_south_geocentric(self): self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0], coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), -68.58362251, places=3) - self.assertAlmostEqual(float(self.out[1]), 20.56981238, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], -68.58362251, places=3) + self.assertAlmostEqual(self.out[1][0], 20.56981238, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_south_geodetic(self): @@ -873,9 +879,9 @@ def test_normal_coord_south_geodetic(self): self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0], coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), -68.53149555, places=3) - self.assertAlmostEqual(float(self.out[1]), 20.57270224, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], -68.53149555, places=3) + self.assertAlmostEqual(self.out[1][0], 20.57270224, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_normal_coord_south_corrected(self): @@ -884,17 +890,17 @@ def test_normal_coord_south_corrected(self): self.ocb.rfunc_kwargs[self.ocb.rec_ind]['r_add'] = self.r_corr self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0]) - self.assertAlmostEqual(float(self.out[0]), -87.0909090909091, places=3) - self.assertAlmostEqual(float(self.out[1]), 6.0, places=3) - self.assertEqual(float(self.out[2]), self.r_corr) + self.assertAlmostEqual(self.out[0][0], -87.0909090909091, places=3) + self.assertAlmostEqual(self.out[1][0], 6.0, places=3) + self.assertEqual(self.out[2][0], self.r_corr) return def test_revert_coord_south(self): """Test the reversion to AACGM coordinates in the south.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr) - self.assertAlmostEqual(float(self.out[0]), self.lat[1]) - self.assertAlmostEqual(float(self.out[1]), self.mlt[1]) + self.assertAlmostEqual(self.out[0][0], self.lat[1]) + self.assertAlmostEqual(self.out[1][0], self.mlt[1]) return def test_revert_coord_south_array(self): @@ -922,24 +928,24 @@ def test_revert_coord_south_coord_label(self): """Test reversion to AACGM coordinates in the south with Mag label.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr, coords='MAG') - self.assertAlmostEqual(float(self.out[0]), self.lat[1]) - self.assertAlmostEqual(float(self.out[1]), self.mlt[1]) + self.assertAlmostEqual(self.out[0][0], self.lat[1]) + self.assertAlmostEqual(self.out[1][0], self.mlt[1]) return def test_revert_coord_south_geodetic(self): """Test the reversion to geodetic coordinates in the south.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr, coords='geodetic') - self.assertAlmostEqual(float(self.out[0]), -59.17923691, places=3) - self.assertAlmostEqual(float(self.out[1]), 6.61724772, places=3) + self.assertAlmostEqual(self.out[0][0], -59.17923691, places=3) + self.assertAlmostEqual(self.out[1][0], 6.61724772, places=3) return def test_revert_coord_south_geocentric(self): """Test the reversion to geocentric coordinates in the south.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr, coords='geocentric') - self.assertAlmostEqual(float(self.out[0]), -59.01868904, places=3) - self.assertAlmostEqual(float(self.out[1]), 6.61724772, places=3) + self.assertAlmostEqual(self.out[0][0], -59.01868904, places=3) + self.assertAlmostEqual(self.out[1][0], 6.61724772, places=3) return def test_revert_coord_south_w_north(self): From 03464941824ebed38fa7e780063df6799e759d90 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 27 Mar 2024 15:41:13 -0400 Subject: [PATCH 31/73] BUG: fixed bugs in unit tests Fixed bugs in the unit tests. --- ocbpy/tests/test_boundary_eab.py | 6 +++--- ocbpy/tests/test_boundary_ocb.py | 26 +++++++++++++------------- ocbpy/tests/test_pysat.py | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ocbpy/tests/test_boundary_eab.py b/ocbpy/tests/test_boundary_eab.py index 76244d12..d538dbed 100644 --- a/ocbpy/tests/test_boundary_eab.py +++ b/ocbpy/tests/test_boundary_eab.py @@ -206,9 +206,9 @@ def test_normal_coord_south_corrected(self): self.ocb.rfunc_kwargs[self.ocb.rec_ind]['r_add'] = self.r_corr self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0]) - self.assertAlmostEqual(self.out[0][0], -84.764005494, places=3) - self.assertAlmostEqual(self.out[1][0], 6.02245269240, places=3) - self.assertEqual(self.out[2][0], self.r_corr) + self.assertAlmostEqual(self.out[0], -84.764005494, places=3) + self.assertAlmostEqual(self.out[1], 6.02245269240, places=3) + self.assertEqual(self.out[2], self.r_corr) return def test_aacgm_boundary_location_good_south(self): diff --git a/ocbpy/tests/test_boundary_ocb.py b/ocbpy/tests/test_boundary_ocb.py index bb688023..ab118353 100644 --- a/ocbpy/tests/test_boundary_ocb.py +++ b/ocbpy/tests/test_boundary_ocb.py @@ -447,9 +447,9 @@ def test_normal_coord_north_float(self): """Test the normalisation calculation in the north.""" self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1]) - self.assertAlmostEqual(self.out[0][0], self.ocb_lat[-1]) - self.assertAlmostEqual(self.out[1][0], self.ocb_mlt[-1]) - self.assertEqual(self.out[2][0], self.r_corr) + self.assertAlmostEqual(self.out[0], self.ocb_lat[-1]) + self.assertAlmostEqual(self.out[1], self.ocb_mlt[-1]) + self.assertEqual(self.out[2], self.r_corr) return def test_normal_coord_north_array(self): @@ -477,9 +477,9 @@ def test_normal_coord_north_alt_mag_label(self): self.set_ocb() self.out = self.ocb.normal_coord(self.lat[-1], self.mlt[-1], coords='Mag') - self.assertAlmostEqual(self.out[0][0], self.ocb_lat[-1]) - self.assertAlmostEqual(self.out[1][0], self.ocb_mlt[-1]) - self.assertEqual(self.out[2][0], self.r_corr) + self.assertAlmostEqual(self.out[0], self.ocb_lat[-1]) + self.assertAlmostEqual(self.out[1], self.ocb_mlt[-1]) + self.assertEqual(self.out[2], self.r_corr) return def test_normal_coord_north_geodetic(self): @@ -890,17 +890,17 @@ def test_normal_coord_south_corrected(self): self.ocb.rfunc_kwargs[self.ocb.rec_ind]['r_add'] = self.r_corr self.out = self.ocb.normal_coord(self.lat[0], self.mlt[0]) - self.assertAlmostEqual(self.out[0][0], -87.0909090909091, places=3) - self.assertAlmostEqual(self.out[1][0], 6.0, places=3) - self.assertEqual(self.out[2][0], self.r_corr) + self.assertAlmostEqual(self.out[0], -87.0909090909091, places=3) + self.assertAlmostEqual(self.out[1], 6.0, places=3) + self.assertEqual(self.out[2], self.r_corr) return def test_revert_coord_south(self): """Test the reversion to AACGM coordinates in the south.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr) - self.assertAlmostEqual(self.out[0][0], self.lat[1]) - self.assertAlmostEqual(self.out[1][0], self.mlt[1]) + self.assertAlmostEqual(self.out[0], self.lat[1]) + self.assertAlmostEqual(self.out[1], self.mlt[1]) return def test_revert_coord_south_array(self): @@ -928,8 +928,8 @@ def test_revert_coord_south_coord_label(self): """Test reversion to AACGM coordinates in the south with Mag label.""" self.out = self.ocb.revert_coord(self.ocb_lat[1], self.ocb_mlt[1], self.r_corr, coords='MAG') - self.assertAlmostEqual(self.out[0][0], self.lat[1]) - self.assertAlmostEqual(self.out[1][0], self.mlt[1]) + self.assertAlmostEqual(self.out[0], self.lat[1]) + self.assertAlmostEqual(self.out[1], self.mlt[1]) return def test_revert_coord_south_geodetic(self): diff --git a/ocbpy/tests/test_pysat.py b/ocbpy/tests/test_pysat.py index d2859aa5..d8e66ec9 100644 --- a/ocbpy/tests/test_pysat.py +++ b/ocbpy/tests/test_pysat.py @@ -598,7 +598,7 @@ def test_add_ocb_to_data_no_file(self): ocb_pysat.add_ocb_to_data(self.test_inst, self.pysat_lat, "mlt", **self.ocb_kw, max_sdiff=self.del_time) - self.lwarn = u"no data in Boundary file(s)" + self.lwarn = u"no data in Boundary file" self.eval_logging_message() return @@ -998,7 +998,7 @@ def test_cust_add_ocb_to_data_no_file(self): self.test_load() # Test the logging output - self.lwarn = u'no data in Boundary file(s)' + self.lwarn = u'no data in Boundary file' self.eval_logging_message() return From ebe986e7f2888b1e15b7734c97e41b9f2cda046c Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 09:39:39 -0400 Subject: [PATCH 32/73] DOC: fixed typo in example Fixed the typo in the pysat example. --- docs/examples/ex_pysat_eab.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/examples/ex_pysat_eab.rst b/docs/examples/ex_pysat_eab.rst index 805ab12f..f1bd78e3 100644 --- a/docs/examples/ex_pysat_eab.rst +++ b/docs/examples/ex_pysat_eab.rst @@ -74,8 +74,8 @@ using the following commands. # Replace `user` with a string holding your name and `password` with your # email. Madrigal uses these to demonstrate their utility to funders. - tec = pysat.Instrumet(inst_module=py_mad.instruments.gnss_tec, tag='vtec', - user=user, password=password) + tec = pysat.Instrument(inst_module=py_mad.instruments.gnss_tec, tag='vtec', + user=user, password=password) tec.download(start=eab.dtime[eab.rec_ind]) print(tec.files.files) From cbbac41570464d57bbb5f6ffd9e4696768ee9dac Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 09:48:04 -0400 Subject: [PATCH 33/73] DOC: updated changelog Added a summary of changes to the changelog. --- Changelog.rst | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/Changelog.rst b/Changelog.rst index bef61cd1..fa921347 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -3,13 +3,22 @@ Changelog Summary of all changes made since the first stable release -0.X.X (XX-XX-2023) +0.4.0 (XX-XX-2024) ------------------ * DEP: Deprecated functions that depend on ssj_auroral_boundary_package * DEP: Removed classes and kwargs deprecated in v0.3.0 -* MAINT: Added a pyproject.toml file +* MAINT: Added a pyproject.toml file and removed setup.py +* MAINT: Updated numpy logic to address DeprecationWarnings +* MAINT: Updated GitHub Action yamls to use pyproject.toml and cycle versions * ENH: Added AMPERE OCBs up to 2022, using updated AMPERE data * ENH: Added AMPERE EABs, using the Heppner-Maynard boundary as a valid EAB +* ENH: Changed default directory ID to use `pathlib` +* ENH: Allow data padding in `pysat_instrument` functions +* BUG: Fixed a typo in the documentation's pysat example +* BUG: Added an error catch for badly formatted SuperMAG file reading +* TST: Added a new CI test for the Test PyPi Release Candidate +* TST: Reduced duplication by creating a common test class and test variable +* DOC: Improved docstring style compliance and correctness 0.3.0 (10-21-2022) ------------------ From 0e3d0787828183585216782f0175707f17046210 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 09:51:55 -0400 Subject: [PATCH 34/73] TST: added a readthedocs yaml Added a yaml for running RTD. --- .readthedocs.yml | 24 ++++++++++++++++++++++++ Changelog.rst | 1 + 2 files changed, 25 insertions(+) create mode 100644 .readthedocs.yml diff --git a/.readthedocs.yml b/.readthedocs.yml new file mode 100644 index 00000000..9a6b77b1 --- /dev/null +++ b/.readthedocs.yml @@ -0,0 +1,24 @@ +# .readthedocs.yml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +build: + os: ubuntu-22.04 + tools: + python: "3.11" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/conf.py + +# Declare the Python requirements required to build your docs +# This method includes a local build of the package +python: + install: + - method: pip + path: . + extra_requirements: + - doc diff --git a/Changelog.rst b/Changelog.rst index fa921347..6c7cfe96 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -18,6 +18,7 @@ Summary of all changes made since the first stable release * BUG: Added an error catch for badly formatted SuperMAG file reading * TST: Added a new CI test for the Test PyPi Release Candidate * TST: Reduced duplication by creating a common test class and test variable +* TST: Added a ReadTheDocs yaml * DOC: Improved docstring style compliance and correctness 0.3.0 (10-21-2022) From 651dfa9d345561b048c7c8f11b053e313f4c5e46 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:05:18 -0400 Subject: [PATCH 35/73] STY: added return to all methods Added a return call to all methods. --- ocbpy/tests/test_ocb_time.py | 52 +++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/ocbpy/tests/test_ocb_time.py b/ocbpy/tests/test_ocb_time.py index 4f4f9c92..e831cd69 100644 --- a/ocbpy/tests/test_ocb_time.py +++ b/ocbpy/tests/test_ocb_time.py @@ -17,18 +17,19 @@ class TestOCBTimeMethods(unittest.TestCase): def setUp(self): """Set up test runs.""" - self.dtime = dt.datetime(2001, 1, 1) self.dtime2 = dt.datetime(1901, 1, 1) + return def tearDown(self): """Clean up after each test.""" - del self.dtime, self.dtime2 + return def test_year_soy_to_datetime(self): """Test to see that the seconds of year conversion works.""" self.assertEqual(ocb_time.year_soy_to_datetime(2001, 0), self.dtime) + return def test_convert_time_date_tod(self): """Test to see that the default datetime construction works.""" @@ -36,6 +37,7 @@ def test_convert_time_date_tod(self): self.assertEqual(ocb_time.convert_time(date="2001-01-01", tod="00:00:00"), self.dtime) + return def test_convert_time_date_tod_uncoverted(self): """Test the datetime construction with unconverted data.""" @@ -43,6 +45,7 @@ def test_convert_time_date_tod_uncoverted(self): self.assertEqual(ocb_time.convert_time(date="2001-01-01", tod="00:00:00.000001"), self.dtime) + return def test_convert_time_date_tod_fmt(self): """Test to see that the datetime construction works with custom format. @@ -53,11 +56,13 @@ def test_convert_time_date_tod_fmt(self): ocb_time.convert_time(date="2001-01-01", tod="00-00-00", datetime_fmt="%Y-%m-%d %H-%M-%S"), self.dtime) + return def test_convert_time_year_soy(self): """Test to see that the datetime construction works with year-soy.""" # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(year=2001, soy=0), self.dtime) + return def test_convert_time_yyddd_tod(self): """Test to see that the datetime construction works with yyddd and tod. @@ -66,6 +71,7 @@ def test_convert_time_yyddd_tod(self): # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", tod="00:00:00"), self.dtime) + return def test_convert_time_yyddd_tod_w_fmt(self): """Test the datetime construction with yyddd, tod, and datetime_fmt.""" @@ -73,6 +79,7 @@ def test_convert_time_yyddd_tod_w_fmt(self): self.assertEqual(ocb_time.convert_time(yyddd="101001", tod="00 00 00", datetime_fmt="YYDDD %H %M %S"), self.dtime) + return def test_convert_time_yyddd_tod_w_time_fmt(self): """Test the datetime construction with yyddd, tod, and time fmt.""" @@ -80,6 +87,7 @@ def test_convert_time_yyddd_tod_w_time_fmt(self): self.assertEqual(ocb_time.convert_time(yyddd="101001", tod="00 00 00", datetime_fmt="%H %M %S"), self.dtime) + return def test_convert_time_yyddd_sod(self): """Test to see that the datetime construction works with yyddd and sod. @@ -88,13 +96,16 @@ def test_convert_time_yyddd_sod(self): # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", sod=0), self.dtime) + return def test_convert_time_yyddd_sod_ms(self): """Test the datetime construction works with yyddd, sod, and msec.""" self.dtime = self.dtime.replace(microsecond=1) + # Test the year-soy implimentation self.assertEqual(ocb_time.convert_time(yyddd="101001", sod=1.0e-6), self.dtime) + return def test_convert_time_dict_input(self): """Test to see that the datetime construction works with dict inputs.""" @@ -110,47 +121,56 @@ def test_convert_time_dict_input(self): self.assertEqual(ocb_time.convert_time(**input_dict), self.dtime) del input_dict + return def test_convert_time_failure_yyddd(self): """Test convert_time failure with non-string input for yyddd.""" with self.assertRaisesRegex(ValueError, "YYDDD must be a string"): ocb_time.convert_time(yyddd=101001) + return def test_convert_time_failure_soy(self): """Test convert_time failure with bad input for year-soy.""" with self.assertRaisesRegex(ValueError, "does not match format"): ocb_time.convert_time(soy=200) + return def test_convert_time_failure_bad_date_fmt(self): """Test convert_time failure with incorrect date format.""" with self.assertRaisesRegex(ValueError, "does not match format"): ocb_time.convert_time(date="2000", tod="00") + return def test_yyddd_to_date(self): """Test to see that the datetime construction works.""" # Test the year-soy implimentation for 2001 and 1901 self.assertEqual(ocb_time.yyddd_to_date(yyddd="101001"), self.dtime) self.assertEqual(ocb_time.yyddd_to_date(yyddd="01001"), self.dtime2) + return def test_yyddd_to_date_failure(self): """Test yyddd_to_date failure with non-string input.""" with self.assertRaisesRegex(ValueError, "YYDDD must be a string"): ocb_time.yyddd_to_date(yyddd=101001) + return def test_datetime2hr(self): """Test datetime to hour of day conversion.""" self.assertEqual(ocb_time.datetime2hr(self.dtime), 0.0) + return def test_datetime2hr_all_fracs(self): """Test datetime to hour of day conversion for a time with h,m,s,ms.""" self.dtime = self.dtime.replace(hour=1, minute=1, second=1, microsecond=1) self.assertAlmostEqual(ocb_time.datetime2hr(self.dtime), 1.01694444472) + return def test_datetime2hr_input_failure(self): """Test datetime to hour of day conversion with bad input.""" with self.assertRaises(AttributeError): ocb_time.datetime2hr(5.0) + return class TestOCBTimeUnits(unittest.TestCase): @@ -161,10 +181,12 @@ def setUp(self): self.lon = np.linspace(0.0, 360.0, 37) self.lt = np.linspace(0.0, 24.0, 37) self.out = None + return def tearDown(self): """Clean up after each test.""" del self.lon, self.lt, self.out + return def test_deg2hr_array(self): """Test degree to hour conversion for an array.""" @@ -172,12 +194,14 @@ def test_deg2hr_array(self): for i, val in enumerate(self.lt): self.assertAlmostEqual(self.out[i], val) + return def test_deg2hr_value(self): """Test degree to hour conversion for a single value.""" self.out = ocb_time.deg2hr(self.lon[0]) self.assertAlmostEqual(self.out, self.lt[0]) + return def test_hr2deg_array(self): """Test hour to degree conversion for an array.""" @@ -185,12 +209,14 @@ def test_hr2deg_array(self): for i, val in enumerate(self.lon): self.assertAlmostEqual(self.out[i], val) + return def test_hr2deg_value(self): """Test hour to degree conversion for a single value.""" self.out = ocb_time.deg2hr(self.lt[0]) self.assertAlmostEqual(self.out, self.lon[0]) + return def test_hr2rad_array(self): """Test hour to radian conversion for an array.""" @@ -198,12 +224,14 @@ def test_hr2rad_array(self): for i, val in enumerate(np.radians(self.lon)): self.assertAlmostEqual(self.out[i], val) + return def test_hr2rad_value(self): """Test hour to radian conversion for a single value.""" self.out = ocb_time.hr2rad(self.lt[0]) self.assertAlmostEqual(self.out, np.radians(self.lon[0])) + return def test_rad2hr_array(self): """Test radian to hour conversion for an array.""" @@ -211,12 +239,14 @@ def test_rad2hr_array(self): for i, val in enumerate(self.out): self.assertAlmostEqual(val, self.lt[i]) + return def test_rad2hr_value(self): """Test radian to hour conversion for a single value.""" self.out = ocb_time.rad2hr(np.radians(self.lon[0])) self.assertAlmostEqual(self.out, self.lt[0]) + return class TestOCBGeographicTime(unittest.TestCase): @@ -228,10 +258,12 @@ def setUp(self): self.lon = [390.0, 359.0, 90.0, -15.0, -30.0] self.lt = [27.0, 0.9333333333333336, 7.0, 0.0, -1.0] self.out = list() + return def tearDown(self): """Clean up after each test.""" del self.lon, self.lt, self.dtime, self.out + return def test_glon2slt(self): """Test longitude to slt conversion for a range of values.""" @@ -244,6 +276,7 @@ def test_glon2slt(self): for i, lon in enumerate(self.lon): self.assertAlmostEqual(ocb_time.glon2slt(lon, self.dtime), self.out[i]) + return def test_slt2glon(self): """Test slt to longitude conversion for a range of values.""" @@ -255,6 +288,7 @@ def test_slt2glon(self): for i, lt in enumerate(self.lt): self.assertAlmostEqual(ocb_time.slt2glon(lt, self.dtime), self.out[i]) + return def test_slt2glon_list(self): """Test slt to longitude conversion for a list of values.""" @@ -267,6 +301,7 @@ def test_slt2glon_list(self): for i, ll in enumerate(self.out): self.assertAlmostEqual(ll, self.lon[i]) + return def test_slt2glon_array(self): """Test slt to longitude conversion for an array of values.""" @@ -279,6 +314,7 @@ def test_slt2glon_array(self): for i, ll in enumerate(self.out): self.assertAlmostEqual(ll, self.lon[i]) + return def test_glon2slt_list(self): """Test longtiude to lt conversion with list input.""" @@ -291,6 +327,7 @@ def test_glon2slt_list(self): for i, ll in enumerate(self.out): self.assertAlmostEqual(ll, self.lt[i]) + return def test_glon2slt_array(self): """Test longtiude to lt conversion with array input.""" @@ -303,6 +340,7 @@ def test_glon2slt_array(self): for i, ll in enumerate(self.out): self.assertAlmostEqual(ll, self.lt[i]) + return class TestTimeFormatMethods(unittest.TestCase): @@ -315,10 +353,12 @@ def setUp(self): '%A %B %z %Z', '%c', '%j %x', '%X'] self.out_fmt = u'' self.out_len = 0 + return def tearDown(self): """Clean up after each test.""" del self.dt_formats, self.dtime, self.out_fmt, self.out_len + return def test_get_datetime_fmt_len(self): """Test the datetime format length determination.""" @@ -334,6 +374,7 @@ def test_get_datetime_fmt_len(self): # Test to see that the returned length is greater than or # equal to the formatted time string self.assertGreaterEqual(self.out_len, len(self.out_fmt)) + return class TestFixRange(unittest.TestCase): @@ -341,24 +382,26 @@ class TestFixRange(unittest.TestCase): def setUp(self): """Set up test runs.""" - self.vals = np.linspace(-190.0, 360.0, 37) self.out = None + return def tearDown(self): """Clean up after each test.""" - del self.vals, self.out + return def test_fix_range_max_min_failure(self): """Test fix_range failure with bad max/min input.""" with self.assertRaisesRegex(ValueError, "Minimum is not less than"): ocb_time.fix_range(self.vals, 10.0, -10.0) + return def test_fix_range_range_failure(self): """Test fix_range failure with bad value range input.""" with self.assertRaisesRegex(ValueError, "Value range must be greater"): ocb_time.fix_range(self.vals, -10.0, 10.0, 0.0) + return def test_fix_range(self): """Test fix_range success.""" @@ -372,3 +415,4 @@ def test_fix_range(self): self.assertTrue(np.all(np.greater_equal(self.out, tset[0][1]))) self.assertTrue(np.all(np.less(self.out, tset[0][2]))) + return From c22ebfea5faa4f0c64121a6744acbf6498ccb162 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:05:34 -0400 Subject: [PATCH 36/73] STY: added an end line Added a newline to the end of the file. --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 8b8742d6..7b5e2028 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,4 +6,4 @@ version = 0.3.0 max-line-length = 80 ignore = E275 - W503 \ No newline at end of file + W503 From f890a5bbdc4e8c1bfd24e28662f76bb368af3e6a Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:13:57 -0400 Subject: [PATCH 37/73] BUG: fixed toml format Fixed the toml format, removing all bare strings. --- pyproject.toml | 44 +++++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cc4242ea..841518c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,8 +1,8 @@ [build-system] requires = [ - "oldest-supported-numpy", - "setuptools", - "wheel", + "oldest-supported-numpy", + "setuptools", + "wheel", ] [project] @@ -11,8 +11,7 @@ name = "ocbpy" license = {file = "LICENSE"} description = 'Location relative to open/closed field line boundary' maintainers = [ - {name = "Angeline G. Burrell", - email = "angeline.g.burrell.civ@us.navy.mil"}, + {name = "Angeline G. Burrell", email = "angeline.g.burrell.civ@us.navy.mil"}, ] requires-python = ">=3.7" dependencies = [ @@ -84,26 +83,25 @@ documentation = "https://ocbpy.readthedocs.io/en/latest/" tracker = "https://github.com/aburrell/ocbpy/issues" download = "https://github.com/aburrell/ocbpy/releases" - [tool.coverage.run] -relative_files = True -include = */ocbpy/* - */ocbpy/tests/* +relative_files = true +include = ["*/ocbpy/*", + "*/ocbpy/tests/*"] [tool.coverage.paths] -ocb_paths = ocbpy/ - */lib/*/site-packages/ocbpy +ocb_paths = ["ocbpy/", + "*/lib/*/site-packages/ocbpy"] [tool.coverage.report] -omit = */lib/*/site-packages/*.py - */lib/*/site-packages/a*/* - */lib/*/site-packages/c*/* - */lib/*/site-packages/d*/* - */lib/*/site-packages/g*/* - */lib/*/site-packages/i*/* - */lib/*/site-packages/m*/* - */lib/*/site-packages/n*/* - */lib/*/site-packages/p*/* - */lib/*/site-packages/r*/* - */lib/*/site-packages/s*/* - */lib/*/site-packages/u*/* +omit = ["*/lib/*/site-packages/*.py", + "*/lib/*/site-packages/a*/*", + "*/lib/*/site-packages/c*/*", + "*/lib/*/site-packages/d*/*", + "*/lib/*/site-packages/g*/*", + "*/lib/*/site-packages/i*/*", + "*/lib/*/site-packages/m*/*", + "*/lib/*/site-packages/n*/*", + "*/lib/*/site-packages/p*/*", + "*/lib/*/site-packages/r*/*", + "*/lib/*/site-packages/s*/*", + "*/lib/*/site-packages/u*/*"] From 78d77d2a9ed4900dc818a21bf1f6ea8f688bca3e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:18:59 -0400 Subject: [PATCH 38/73] STY: added missing blank line Added a missing blank line before a new function. --- ocbpy/instruments/pysat_instruments.py | 1 + 1 file changed, 1 insertion(+) diff --git a/ocbpy/instruments/pysat_instruments.py b/ocbpy/instruments/pysat_instruments.py index d2b10a74..950414f4 100644 --- a/ocbpy/instruments/pysat_instruments.py +++ b/ocbpy/instruments/pysat_instruments.py @@ -485,6 +485,7 @@ def add_ocb_to_data(pysat_inst, mlat_name='', mlt_name='', evar_names=None, return + def reshape_pad_mask_flatten(data, mask): """Reshape, pad, mask, and flatten data. From fc34717c998946b04cbeaf941f438fa5360f366d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:24:41 -0400 Subject: [PATCH 39/73] BUG: update coverage install The coverage module requires extra dependencies to work with a toml file. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 841518c2..26351ea1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -71,7 +71,7 @@ doc = [ "numpydoc", ] test = [ - "coverage", + "coverage[toml]", "coveralls", "flake8", "packaging", From 24ff591f53520fdaa7ff8c67a717250054f74c5d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:35:21 -0400 Subject: [PATCH 40/73] DOC: update redirecting links Updated the links and email in the code of conduct. --- CODE_OF_CONDUCT.md | 55 ++++++++++++++++++++++++++++++++++----------- CODE_OF_CONDUCT.rst | 10 ++++----- 2 files changed, 47 insertions(+), 18 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c307f5b4..bfdd741f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,11 +2,17 @@ ## Our Pledge -In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of +experience, nationality, personal appearance, race, religion, or sexual +identity and orientation. ## Our Standards -Examples of behavior that contributes to creating a positive environment include: +Examples of behavior that contributes to creating a positive environment +include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences @@ -16,31 +22,54 @@ Examples of behavior that contributes to creating a positive environment include Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or advances +* The use of sexualized language or imagery and unwelcome sexual attention or + advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment -* Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a professional setting +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting ## Our Responsibilities -Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. -Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions that +are not aligned to this Code of Conduct, or to ban temporarily or permanently +any contributor for other behaviors that they deem inappropriate, threatening, +offensive, or harmful. ## Scope -This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may +be further defined and clarified by project maintainers. ## Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at agb073000@utdallas.edu. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at agb073000@utdallas.edu. The project +team will review and investigate all complaints, and will respond in a way that +it deems appropriate to the circumstances. The project team is obligated to +maintain confidentiality with regard to the reporter of an incident. Further +details of specific enforcement policies may be posted separately. -Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 1.4, available at +[https://www.contributor-covenant.org/version/1/4/][version] -[homepage]: https://contributor-covenant.org -[version]: https://contributor-covenant.org/version/1/4/ +[homepage]: https://www.contributor-covenant.org/ +[version]: https://www.contributor-covenant.org/version/1/4/ diff --git a/CODE_OF_CONDUCT.rst b/CODE_OF_CONDUCT.rst index 5a1275c0..00e1f6eb 100644 --- a/CODE_OF_CONDUCT.rst +++ b/CODE_OF_CONDUCT.rst @@ -61,9 +61,9 @@ Enforcement ----------- Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at angeline.burrell@nrl.navy.mil. The -project team will review and investigate all complaints, and will respond in a -way that it deems appropriate to the circumstances. The project team is +reported by contacting the project team at angeline.g.burrell.civ@us.navy.mil. +The project team will review and investigate all complaints, and will respond in +a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. @@ -78,5 +78,5 @@ Attribution This Code of Conduct is adapted from the Contributor Covenant [homepage]_, version [1.4]_. -.. [homepage] https://contributor-covenant.org -.. [1.4] https://contributor-covenant.org/version/1/4/ +.. [homepage] https://www.contributor-covenant.org/ +.. [1.4] https://www.contributor-covenant.org/version/1/4/ From 4d6de17755c5b272ade55c8d2c2a12df43aa4389 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:38:52 -0400 Subject: [PATCH 41/73] DOC: updated email Updated my email in the markdown code of conduct. --- CODE_OF_CONDUCT.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index bfdd741f..e94b2e5e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -55,11 +55,12 @@ be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at agb073000@utdallas.edu. The project -team will review and investigate all complaints, and will respond in a way that -it deems appropriate to the circumstances. The project team is obligated to -maintain confidentiality with regard to the reporter of an incident. Further -details of specific enforcement policies may be posted separately. +reported by contacting the project team at angeline.g.burrell.civ@us.navy.mil. +The project team will review and investigate all complaints, and will respond in +a way that it deems appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an +incident. Further details of specific enforcement policies may be posted +separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other From 9fd31d909f97aee954b79d195195bf2d215e6850 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:41:04 -0400 Subject: [PATCH 42/73] STY: Update CODE_OF_CONDUCT.md Removed extra whitespace. --- CODE_OF_CONDUCT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index e94b2e5e..154beae7 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -72,5 +72,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://www.contributor-covenant.org/version/1/4/][version] -[homepage]: https://www.contributor-covenant.org/ +[homepage]: https://www.contributor-covenant.org/ [version]: https://www.contributor-covenant.org/version/1/4/ From 00d291d99069c3b2a8e0125694d7eb6fdb21b3d9 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 10:44:31 -0400 Subject: [PATCH 43/73] DOC: updated index depth Reduced the index depth for a more legible first page. --- docs/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.rst b/docs/index.rst index 6aff1c0b..f1cec547 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -10,7 +10,7 @@ This documentation describes the Open Closed field line Boundary (OCB) gridding process and provides examples for usage. .. toctree:: - :maxdepth: -1 + :maxdepth: 2 overview.rst citing.rst From 6537dcb076796f443555db34e9cb5c270666aa07 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 28 Mar 2024 13:06:56 -0400 Subject: [PATCH 44/73] BUG: update coveralls Update coveralls, since this didn't trigger a coverage analysis before. --- .github/workflows/main.yml | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 9c5794e4..c5e31004 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -63,20 +63,13 @@ jobs: run: coveralls --rcfile=pyproject.toml --service=github || true finish: + name: Finish Coverage Analysis needs: build runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.11"] steps: - - uses: actions/checkout@v4 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - name: Coveralls Finished env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | pip install --upgrade coveralls - coveralls --service=github --finish || true + coveralls --service=github --finish From f042669a8dbc4d2d1fca55d73fbd5a45056b7f18 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 10:10:21 -0400 Subject: [PATCH 45/73] BUG: Update .github/workflows/main.yml Update coveralls call. Co-authored-by: Jeff Klenzing <19592220+jklenzing@users.noreply.github.com> --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c5e31004..fd503299 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -60,7 +60,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.install-extras }} - run: coveralls --rcfile=pyproject.toml --service=github || true + run: coveralls --rcfile=pyproject.toml --service=github finish: name: Finish Coverage Analysis From 0d6d3791fe83ebd2510f77bed149bdabf7c45857 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 10:17:37 -0400 Subject: [PATCH 46/73] BUG: use true pipe Use true pipe with coveralls. --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fd503299..793398b2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -60,7 +60,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.install-extras }} - run: coveralls --rcfile=pyproject.toml --service=github + run: coveralls --rcfile=pyproject.toml --service=github || true finish: name: Finish Coverage Analysis @@ -72,4 +72,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | pip install --upgrade coveralls - coveralls --service=github --finish + coveralls --service=github --finish || true From bca742304d5f91014fc96f94aea10e787c6f6329 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 11:11:06 -0400 Subject: [PATCH 47/73] BUG: try coverallsapp Try coveralls app for GitHub Actions and see if that fixes things. --- .github/workflows/main.yml | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 793398b2..7acfc598 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,20 +56,18 @@ jobs: run: coverage run --rcfile=pyproject.toml -m unittest discover - name: Publish results to coveralls upon success - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_PARALLEL: true - COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.install-extras }} - run: coveralls --rcfile=pyproject.toml --service=github || true + uses: coverallsapp/github-action@v2 + with: + flag-name: run-${{ join(matrix.*, '-') }} + parallel: true finish: name: Finish Coverage Analysis needs: build + if: ${{ always() }} runs-on: ubuntu-latest steps: - name: Coveralls Finished - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - pip install --upgrade coveralls - coveralls --service=github --finish || true + uses: coverallsapp/github-action@v2 + with: + parallel-finished: true From fe537ce5dd0310ae8f4b148c5a167880c573286e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 12:43:58 -0400 Subject: [PATCH 48/73] Revert "BUG: try coverallsapp" This reverts commit bca742304d5f91014fc96f94aea10e787c6f6329. --- .github/workflows/main.yml | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 7acfc598..793398b2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,18 +56,20 @@ jobs: run: coverage run --rcfile=pyproject.toml -m unittest discover - name: Publish results to coveralls upon success - uses: coverallsapp/github-action@v2 - with: - flag-name: run-${{ join(matrix.*, '-') }} - parallel: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.install-extras }} + run: coveralls --rcfile=pyproject.toml --service=github || true finish: name: Finish Coverage Analysis needs: build - if: ${{ always() }} runs-on: ubuntu-latest steps: - name: Coveralls Finished - uses: coverallsapp/github-action@v2 - with: - parallel-finished: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pip install --upgrade coveralls + coveralls --service=github --finish || true From 4d66d6937bb6d6a848ada308fc08224af2fcb35f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 12:58:57 -0400 Subject: [PATCH 49/73] BUG: try using coveralls reporter Try using the universal coveralls reporter. --- .github/workflows/main.yml | 27 +++++++++++++++++++++------ pyproject.toml | 1 - 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 793398b2..4b2b478d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,12 +55,29 @@ jobs: - name: Run unit and integration tests run: coverage run --rcfile=pyproject.toml -m unittest discover - - name: Publish results to coveralls upon success + - name: Report Coveralls (Linux) + if: ${{ matrix.os }} == "ubuntu-latest" + run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + + - name: Report Coveralls (Windows) + if: ${{ matrix.os }} == "windows-latest" + run: curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip | zcat > ./coveralls.exe && ./coveralls.exe + env: + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true + + - name: Report Coveralls (macOS) + if: ${{ matrix.os }} == "macos-latest" + run: | + brew tap coverallsapp/coveralls --quiet + brew install coveralls --quiet + coveralls env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true - COVERALLS_FLAG_NAME: run-${{ matrix.python-version }}-${{ matrix.os }}-${{ matrix.install-extras }} - run: coveralls --rcfile=pyproject.toml --service=github || true finish: name: Finish Coverage Analysis @@ -70,6 +87,4 @@ jobs: - name: Coveralls Finished env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - pip install --upgrade coveralls - coveralls --service=github --finish || true + run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls diff --git a/pyproject.toml b/pyproject.toml index 26351ea1..462d7aa6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -72,7 +72,6 @@ doc = [ ] test = [ "coverage[toml]", - "coveralls", "flake8", "packaging", ] From 31607f3d9dc2b2431aa2ede68da033554f24a829 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 13:07:17 -0400 Subject: [PATCH 50/73] BUG: add requests to test requirements The requests package is called explicitly in the DMSP SSJ unit tests. --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 462d7aa6..45600209 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -74,6 +74,7 @@ test = [ "coverage[toml]", "flake8", "packaging", + "requests", ] [project.urls] From 6c8d45168fa37b096ec00ef87a87dea45cef5151 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 13:22:21 -0400 Subject: [PATCH 51/73] BUG: fix yaml if statements Fix the yaml if statements to evaluate correctly. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4b2b478d..d7898972 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -56,21 +56,21 @@ jobs: run: coverage run --rcfile=pyproject.toml -m unittest discover - name: Report Coveralls (Linux) - if: ${{ matrix.os }} == "ubuntu-latest" + if: ${{ matrix.os == "ubuntu-latest" }} run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true - name: Report Coveralls (Windows) - if: ${{ matrix.os }} == "windows-latest" + if: ${{ matrix.os == "windows-latest" }} run: curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip | zcat > ./coveralls.exe && ./coveralls.exe env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true - name: Report Coveralls (macOS) - if: ${{ matrix.os }} == "macos-latest" + if: ${{ matrix.os == "macos-latest" }} run: | brew tap coverallsapp/coveralls --quiet brew install coveralls --quiet From aa2655528ecc8160adfc68b701f71379173a1de1 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 13:42:55 -0400 Subject: [PATCH 52/73] BUG: fix yaml error Fix a different yaml bug. --- .github/workflows/main.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d7898972..c8b18ebc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,7 +12,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: ["ubuntu-latest", "macos-latest", "windows-latest"] python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] install-extras: [0, 1, 2] # TODO(#129): update to replace extra flag exclude: @@ -56,33 +56,33 @@ jobs: run: coverage run --rcfile=pyproject.toml -m unittest discover - name: Report Coveralls (Linux) - if: ${{ matrix.os == "ubuntu-latest" }} - run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls + if: startsWith(matrix.os, 'ubuntu') env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true + run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls - name: Report Coveralls (Windows) - if: ${{ matrix.os == "windows-latest" }} - run: curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip | zcat > ./coveralls.exe && ./coveralls.exe + if: startsWith(matrix.os, 'windows') env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true + run: curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip | zcat > ./coveralls.exe && ./coveralls.exe - name: Report Coveralls (macOS) - if: ${{ matrix.os == "macos-latest" }} + if: startsWith(matrix.os, 'macos') + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_PARALLEL: true run: | brew tap coverallsapp/coveralls --quiet brew install coveralls --quiet coveralls - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_PARALLEL: true finish: name: Finish Coverage Analysis needs: build - runs-on: ubuntu-latest + runs-on: "ubuntu-latest" steps: - name: Coveralls Finished env: From dde77c66bd2520228df25bfacec740bb5bf210c7 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 14:01:07 -0400 Subject: [PATCH 53/73] TST: created a coveralls.yaml MacOS is failing because it needs a token, create a yaml to store the token. --- .coveralls.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml new file mode 100644 index 00000000..92c5338a --- /dev/null +++ b/.coveralls.yml @@ -0,0 +1,2 @@ +# .coveralls.yml +repo_token: yxiLTX3BoE3HDeP0Z6SgH8ssEDBfbRgcM From afa3d25840791f96f036ca8562d15d0e68ec9727 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 14:26:55 -0400 Subject: [PATCH 54/73] BUG: fixed security leak Fixed security leak for Coveralls. --- .coveralls.yml | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .coveralls.yml diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 92c5338a..00000000 --- a/.coveralls.yml +++ /dev/null @@ -1,2 +0,0 @@ -# .coveralls.yml -repo_token: yxiLTX3BoE3HDeP0Z6SgH8ssEDBfbRgcM From 40e2735292284779c677897f8051f13d19b96a48 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 14:27:14 -0400 Subject: [PATCH 55/73] BUG: fixed token bug Fixed token bug and windows execution bug. --- .github/workflows/main.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c8b18ebc..a19c6b42 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,12 +67,14 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true - run: curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip | zcat > ./coveralls.exe && ./coveralls.exe + run: | + curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip | zcat > ./coveralls.exe + ./coveralls.exe - name: Report Coveralls (macOS) if: startsWith(matrix.os, 'macos') env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true run: | brew tap coverallsapp/coveralls --quiet From 7c0939186e75ef8e817e9627650c8e46ae747db0 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 14:44:23 -0400 Subject: [PATCH 56/73] BUG: fix windows command Fix the windows coveralls command attempt 2. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a19c6b42..057fab76 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -16,7 +16,7 @@ jobs: python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] install-extras: [0, 1, 2] # TODO(#129): update to replace extra flag exclude: - - os: windows-latest + - os: "windows-latest" install-extras: 2 name: Python ${{ matrix.python-version }} on ${{ matrix.os }} with extras ${{ matrix.install-extras }} @@ -68,8 +68,8 @@ jobs: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true run: | - curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip | zcat > ./coveralls.exe - ./coveralls.exe + curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip + zcat coveralls-window.zip > ./coveralls.exe && ./coveralls.exe - name: Report Coveralls (macOS) if: startsWith(matrix.os, 'macos') From ef57429298c2d21acd8129d1021e9bccfa0d5506 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 14:49:46 -0400 Subject: [PATCH 57/73] BUG: removed tab from yaml Removed tab from yaml. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 057fab76..31d9aad1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -69,7 +69,7 @@ jobs: COVERALLS_PARALLEL: true run: | curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip - zcat coveralls-window.zip > ./coveralls.exe && ./coveralls.exe + zcat coveralls-window.zip > ./coveralls.exe && ./coveralls.exe - name: Report Coveralls (macOS) if: startsWith(matrix.os, 'macos') From 5c6505ac955a8f012f902c4d1e5496ff02867736 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 15:05:41 -0400 Subject: [PATCH 58/73] BUG: try tar on windows Windows is supposed to have tar from 2017 on, so this may work. --- .github/workflows/main.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 31d9aad1..f63e38ad 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,9 +67,7 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true - run: | - curl -sL https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.zip - zcat coveralls-window.zip > ./coveralls.exe && ./coveralls.exe + run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls - name: Report Coveralls (macOS) if: startsWith(matrix.os, 'macos') From db56e8cd4e227bbbd9df4a8421b679fc62cdbc16 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Fri, 29 Mar 2024 15:16:55 -0400 Subject: [PATCH 59/73] BUG: use README snippet Use the windows snippet from the coverage reporter README instead of the example that isn't working. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f63e38ad..8258945b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -67,7 +67,7 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} COVERALLS_PARALLEL: true - run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls + run: curl -L https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.exe -o coveralls.exe - name: Report Coveralls (macOS) if: startsWith(matrix.os, 'macos') From a64cbda0a47d6e6e1a394d1b55af884cb36ab837 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Mon, 1 Apr 2024 10:21:34 -0400 Subject: [PATCH 60/73] BUG: update for parallel builds Update main.yml for parallel coverage builds. --- .github/workflows/main.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8258945b..bb4048f5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -87,4 +87,6 @@ jobs: - name: Coveralls Finished env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls + run: | + curl -k https://coveralls.io/webhook?repo_token=$GITHUB_TOKEN -d "payload[build_num]=$BUILD_NUMBER&payload[status]=done" + curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls From 03d481b0688144b9b0d9d62146404d7061385856 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Apr 2024 14:09:58 -0400 Subject: [PATCH 61/73] BUG: remove unnecessary download Removed an unnecessary download of coveralls reporter. --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bb4048f5..fc937571 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -89,4 +89,3 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | curl -k https://coveralls.io/webhook?repo_token=$GITHUB_TOKEN -d "payload[build_num]=$BUILD_NUMBER&payload[status]=done" - curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls From d9f7aaeab4687386e2d2a6eadf1f59afa1f9f374 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Apr 2024 15:56:42 -0400 Subject: [PATCH 62/73] BUG: improve coveralls call Improve the coveralls call by: - moving the installation up top, - using the parallel flag, and - explicitly declare the repo token. --- .github/workflows/main.yml | 44 ++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fc937571..e4264765 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,38 +46,33 @@ jobs: pip install .[test] bash requirements.extra ${{ matrix.install-extras }} - - name: Test PEP8 compliance - run: flake8 . --count --show-source --statistics - - - name: Evaluate complexity - run: flake8 . --count --exit-zero --max-complexity=10 --statistics - - - name: Run unit and integration tests - run: coverage run --rcfile=pyproject.toml -m unittest discover - - - name: Report Coveralls (Linux) + - name: Install Coveralls Reporter (Linux) if: startsWith(matrix.os, 'ubuntu') - env: - COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_PARALLEL: true - run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz && ./coveralls + run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz - - name: Report Coveralls (Windows) + - name: Install Coveralls Reporter (Windows) if: startsWith(matrix.os, 'windows') - env: - COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_PARALLEL: true run: curl -L https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.exe -o coveralls.exe - name: Report Coveralls (macOS) if: startsWith(matrix.os, 'macos') - env: - COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} - COVERALLS_PARALLEL: true run: | brew tap coverallsapp/coveralls --quiet brew install coveralls --quiet - coveralls + + - name: Test PEP8 compliance + run: flake8 . --count --show-source --statistics + + - name: Evaluate complexity + run: flake8 . --count --exit-zero --max-complexity=10 --statistics + + - name: Run unit and integration tests + run: coverage run --rcfile=pyproject.toml -m unittest discover + + - name: Report test coverage to Coveralls + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + run: coveralls report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} finish: name: Finish Coverage Analysis @@ -86,6 +81,5 @@ jobs: steps: - name: Coveralls Finished env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - curl -k https://coveralls.io/webhook?repo_token=$GITHUB_TOKEN -d "payload[build_num]=$BUILD_NUMBER&payload[status]=done" + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + run: coveralls done From 7d39757ea4d674e6ac0619d86f57103f3342ccae Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Tue, 2 Apr 2024 16:28:39 -0400 Subject: [PATCH 63/73] BUG: reverted download/call order Different OS versions have different executable names, and so require different commands. --- .github/workflows/main.yml | 42 +++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e4264765..222bdf63 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -46,20 +46,6 @@ jobs: pip install .[test] bash requirements.extra ${{ matrix.install-extras }} - - name: Install Coveralls Reporter (Linux) - if: startsWith(matrix.os, 'ubuntu') - run: curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz - - - name: Install Coveralls Reporter (Windows) - if: startsWith(matrix.os, 'windows') - run: curl -L https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.exe -o coveralls.exe - - - name: Report Coveralls (macOS) - if: startsWith(matrix.os, 'macos') - run: | - brew tap coverallsapp/coveralls --quiet - brew install coveralls --quiet - - name: Test PEP8 compliance run: flake8 . --count --show-source --statistics @@ -69,6 +55,34 @@ jobs: - name: Run unit and integration tests run: coverage run --rcfile=pyproject.toml -m unittest discover + - name: Install and run Coveralls Reporter(Linux) + if: startsWith(matrix.os, 'ubuntu') + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + COVERALLS_PARALLEL: true + run: | + curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz + ./coveralls report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + + - name: Install and run Coveralls Reporter (Windows) + if: startsWith(matrix.os, 'windows') + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + COVERALLS_PARALLEL: true + run: | + curl -L https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.exe -o coveralls.exe + ./coveralls.exe report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + + - name: Report and run Coveralls (macOS) + if: startsWith(matrix.os, 'macos') + env: + COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} + COVERALLS_PARALLEL: true + run: | + brew tap coverallsapp/coveralls --quiet + brew install coveralls --quiet + coveralls report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + - name: Report test coverage to Coveralls env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} From f5c5515311ed837ccdd43a01a9788f453a75280d Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 3 Apr 2024 09:34:56 -0400 Subject: [PATCH 64/73] BUG: removed old code block Removed the old code block that didn't work. --- .github/workflows/main.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 222bdf63..8c4cca1d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -83,11 +83,6 @@ jobs: brew install coveralls --quiet coveralls report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} - - name: Report test coverage to Coveralls - env: - COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} - run: coveralls report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} - finish: name: Finish Coverage Analysis needs: build From 5706bf65a53d889fca056c8b365e5e64910f44d6 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 3 Apr 2024 10:31:25 -0400 Subject: [PATCH 65/73] BUG: fixed final coveralls call Fixed the final coveralls call to use the linux standard. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8c4cca1d..4a97495b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -91,4 +91,4 @@ jobs: - name: Coveralls Finished env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} - run: coveralls done + run: ./coveralls done From e25bd5dde543e51bb80743218f2d7aeeb6976547 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Wed, 3 Apr 2024 11:21:56 -0400 Subject: [PATCH 66/73] BUG: finish requires coveralls download Finish requires coveralls to be downloaded again. --- .github/workflows/main.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4a97495b..ae37ece8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -91,4 +91,7 @@ jobs: - name: Coveralls Finished env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} - run: ./coveralls done + COVERALLS_PARALLEL: true + run: | + curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz + ./coveralls done From ee036aa4a3a5a258b20a69fdb575ff7707ebe878 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Apr 2024 11:47:43 -0400 Subject: [PATCH 67/73] MAINT: updated .gitignore Update .gitignore to include the xml coverage output. --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8baa02a6..f4a4e1dc 100644 --- a/.gitignore +++ b/.gitignore @@ -5,10 +5,11 @@ # Compiled python modules. *.py[cod] -# Test directories +# Test directories and files /build/ .tox .coverage +coverage.xml # Setuptools distribution folder. /dist/ From 96474f4bea64100cbcd00baf5880d424eeedc147 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Apr 2024 11:48:18 -0400 Subject: [PATCH 68/73] BUG: use coverage xml Coveralls-reported does a better job with xml output. --- .github/workflows/main.yml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ae37ece8..15dfe6f5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -53,7 +53,10 @@ jobs: run: flake8 . --count --exit-zero --max-complexity=10 --statistics - name: Run unit and integration tests - run: coverage run --rcfile=pyproject.toml -m unittest discover + run: | + coverage run --rcfile=pyproject.toml -m unittest discover + coverage report + coverage xml --rcfile=pyproject.toml - name: Install and run Coveralls Reporter(Linux) if: startsWith(matrix.os, 'ubuntu') @@ -62,7 +65,7 @@ jobs: COVERALLS_PARALLEL: true run: | curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz - ./coveralls report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + ./coveralls report -format cobertura --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} - name: Install and run Coveralls Reporter (Windows) if: startsWith(matrix.os, 'windows') @@ -71,7 +74,7 @@ jobs: COVERALLS_PARALLEL: true run: | curl -L https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.exe -o coveralls.exe - ./coveralls.exe report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + ./coveralls.exe report -format cobertura --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} - name: Report and run Coveralls (macOS) if: startsWith(matrix.os, 'macos') @@ -81,7 +84,7 @@ jobs: run: | brew tap coverallsapp/coveralls --quiet brew install coveralls --quiet - coveralls report --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + coveralls report -format cobertura --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} finish: name: Finish Coverage Analysis From 7c0faf32d004006e58fca61d8f7dac63f106f03f Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Apr 2024 11:51:21 -0400 Subject: [PATCH 69/73] DOC: udpated changelog Added coveralls update to the changelog. --- Changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Changelog.rst b/Changelog.rst index 6c7cfe96..b1138da2 100644 --- a/Changelog.rst +++ b/Changelog.rst @@ -10,6 +10,7 @@ Summary of all changes made since the first stable release * MAINT: Added a pyproject.toml file and removed setup.py * MAINT: Updated numpy logic to address DeprecationWarnings * MAINT: Updated GitHub Action yamls to use pyproject.toml and cycle versions +* MAINT: Updated GitHub Action yamls to use coveralls-reporter * ENH: Added AMPERE OCBs up to 2022, using updated AMPERE data * ENH: Added AMPERE EABs, using the Heppner-Maynard boundary as a valid EAB * ENH: Changed default directory ID to use `pathlib` From 486796d7386c6416a50c536ae9560a1283d2a165 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Apr 2024 11:51:36 -0400 Subject: [PATCH 70/73] BUG: removed tabs Removed tabs from the yaml. --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 15dfe6f5..fccd7581 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -55,8 +55,8 @@ jobs: - name: Run unit and integration tests run: | coverage run --rcfile=pyproject.toml -m unittest discover - coverage report - coverage xml --rcfile=pyproject.toml + coverage report + coverage xml --rcfile=pyproject.toml - name: Install and run Coveralls Reporter(Linux) if: startsWith(matrix.os, 'ubuntu') From de87059c72e5df2aeae28cd7417026e27c2bf775 Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Apr 2024 12:43:16 -0400 Subject: [PATCH 71/73] BUG: fixed coveralls file specification Specify the coverage XML file instead of a general format. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fccd7581..e162c661 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,7 +65,7 @@ jobs: COVERALLS_PARALLEL: true run: | curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz - ./coveralls report -format cobertura --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + ./coveralls report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} - name: Install and run Coveralls Reporter (Windows) if: startsWith(matrix.os, 'windows') @@ -74,7 +74,7 @@ jobs: COVERALLS_PARALLEL: true run: | curl -L https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.exe -o coveralls.exe - ./coveralls.exe report -format cobertura --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + ./coveralls.exe report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} - name: Report and run Coveralls (macOS) if: startsWith(matrix.os, 'macos') @@ -84,7 +84,7 @@ jobs: run: | brew tap coverallsapp/coveralls --quiet brew install coveralls --quiet - coveralls report -format cobertura --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + coveralls report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} finish: name: Finish Coverage Analysis From f3863996ddbdbd007c7023dc627b428df4df999a Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Apr 2024 14:00:14 -0400 Subject: [PATCH 72/73] BUG: added run number Try adding build number through the GA `run_number` variable to the done call. --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e162c661..34ef1d32 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -97,4 +97,4 @@ jobs: COVERALLS_PARALLEL: true run: | curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz - ./coveralls done + ./coveralls done --build-number ${{ github.run_number }} From 294ddd6d1bbdba0dc76b561cc31543bf3875616e Mon Sep 17 00:00:00 2001 From: Angeline Burrell Date: Thu, 4 Apr 2024 15:46:42 -0400 Subject: [PATCH 73/73] BUG: try more build-number specificiations Try declaring the build-number across the board, to ensure it matches. --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 34ef1d32..4f8f7fdf 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,7 +65,7 @@ jobs: COVERALLS_PARALLEL: true run: | curl -sL https://coveralls.io/coveralls-linux.tar.gz | tar -xz - ./coveralls report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + ./coveralls report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} --build-number ${{ github.run_number }} - name: Install and run Coveralls Reporter (Windows) if: startsWith(matrix.os, 'windows') @@ -74,7 +74,7 @@ jobs: COVERALLS_PARALLEL: true run: | curl -L https://github.com/coverallsapp/coverage-reporter/releases/latest/download/coveralls-windows.exe -o coveralls.exe - ./coveralls.exe report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + ./coveralls.exe report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} --build-number ${{ github.run_number }} - name: Report and run Coveralls (macOS) if: startsWith(matrix.os, 'macos') @@ -84,7 +84,7 @@ jobs: run: | brew tap coverallsapp/coveralls --quiet brew install coveralls --quiet - coveralls report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} + coveralls report -f coverage.xml --parallel --repo-token=${{ secrets.COVERALLS_REPO_TOKEN }} --build-number ${{ github.run_number }} finish: name: Finish Coverage Analysis