From 16f60fd4a028b10bf8094bc84fb444d38c23aa33 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 6 May 2015 22:12:19 +0200 Subject: [PATCH 1/3] Improve test coverage --- reproject/array_utils.py | 2 +- reproject/healpix/tests/test_healpix.py | 14 ++--- reproject/healpix/tests/test_utils.py | 55 +++++++++++++++++++ reproject/healpix/utils.py | 4 +- reproject/spherical_intersect/core.py | 13 +---- reproject/spherical_intersect/high_level.py | 2 +- .../tests/test_high_level.py | 42 ++++++++++++++ 7 files changed, 110 insertions(+), 22 deletions(-) create mode 100644 reproject/healpix/tests/test_utils.py create mode 100644 reproject/spherical_intersect/tests/test_high_level.py diff --git a/reproject/array_utils.py b/reproject/array_utils.py index fa69a9a7a..e8bab95fe 100644 --- a/reproject/array_utils.py +++ b/reproject/array_utils.py @@ -60,7 +60,7 @@ def iterate_over_celestial_slices(array_in, array_out, wcs): def pad_edge_1(array): try: return np.pad(array, 1, mode='edge') - except: # numpy < 1.7 workaround + except: # numpy < 1.7 workaround pragma: no cover new_array = np.zeros((array.shape[0] + 2, array.shape[1] + 2), dtype=array.dtype) diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index af54213f1..9f6761873 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -10,8 +10,7 @@ from astropy.wcs import WCS from astropy.tests.helper import pytest -from ..core import healpix_to_image, image_to_healpix -from ..high_level import reproject_from_healpix +from ..high_level import reproject_from_healpix, reproject_to_healpix from ...tests.test_high_level import ALL_DTYPES DATA = os.path.join(os.path.dirname(__file__), 'data') @@ -55,19 +54,20 @@ def test_reproject_healpix_to_image_round_trip( wcs_out = WCS(reference_header) shape_out = reference_header['NAXIS2'], reference_header['NAXIS1'] - image_data, footprint = healpix_to_image( - healpix_data, healpix_system, wcs_out, shape_out, + image_data, footprint = reproject_from_healpix( + (healpix_data, healpix_system), wcs_out, shape_out=shape_out, order=0, nested=nested) - healpix_data_2, footprint = image_to_healpix( - image_data, wcs_out, healpix_system, - nside, order=0, nested=nested) + healpix_data_2, footprint = reproject_to_healpix( + (image_data, wcs_out), healpix_system, + nside=nside, order=0, nested=nested) np.testing.assert_array_equal(healpix_data, healpix_data_2) @pytest.mark.importorskip('healpy') def test_reproject_file(): + reference_header = get_reference_header(oversample=2, nside=8) data, footprint = reproject_from_healpix(os.path.join(DATA, 'bayestar.fits.gz'), reference_header) reference_result = fits.getdata(os.path.join(DATA, 'reference_result.fits')) diff --git a/reproject/healpix/tests/test_utils.py b/reproject/healpix/tests/test_utils.py new file mode 100644 index 000000000..609fd1fdc --- /dev/null +++ b/reproject/healpix/tests/test_utils.py @@ -0,0 +1,55 @@ +import numpy as np + +from astropy.tests.helper import pytest +from astropy.coordinates import FK5, Galactic, ICRS +from astropy.io import fits + +from ..utils import parse_coord_system, parse_input_healpix_data + +def test_parse_coord_system(): + + frame = parse_coord_system(Galactic()) + assert isinstance(frame, Galactic) + + frame = parse_coord_system('fk5') + assert isinstance(frame, FK5) + + with pytest.raises(ValueError) as exc: + frame = parse_coord_system('e') + assert exc.value.args[0] == "Ecliptic coordinate frame not yet supported" + + frame = parse_coord_system('g') + assert isinstance(frame, Galactic) + + with pytest.raises(ValueError) as exc: + frame = parse_coord_system('spam') + assert exc.value.args[0] == "Could not determine frame for system=spam" + + +def test_parse_input_healpix_data(tmpdir): + + data = np.arange(3072) + + col = fits.Column(array=data, name='flux', format="E") + hdu = fits.BinTableHDU.from_columns([col]) + hdu.header['NSIDE'] = 512 + hdu.header['COORDSYS'] = "G" + + # As HDU + array, coordinate_system = parse_input_healpix_data(hdu) + np.testing.assert_allclose(array, data) + + # As filename + filename = tmpdir.join('test.fits').strpath + hdu.writeto(filename) + array, coordinate_system = parse_input_healpix_data(filename) + np.testing.assert_allclose(array, data) + + # As array + array, coordinate_system = parse_input_healpix_data((data, "galactic")) + np.testing.assert_allclose(array, data) + + # Invalid + with pytest.raises(TypeError) as exc: + parse_input_healpix_data(data) + assert exc.value.args[0] == "input_data should either be an HDU object or a tuple of (array, frame)" diff --git a/reproject/healpix/utils.py b/reproject/healpix/utils.py index 2af3683c3..d3bd591c6 100644 --- a/reproject/healpix/utils.py +++ b/reproject/healpix/utils.py @@ -22,11 +22,11 @@ def parse_coord_system(system): elif system in FRAMES: return FRAMES[system] else: - system_new = frame_transform_graph.lookup_name(system)() + system_new = frame_transform_graph.lookup_name(system) if system_new is None: raise ValueError("Could not determine frame for system={0}".format(system)) else: - return system_new + return system_new() def parse_input_healpix_data(input_data, field=0, hdu_in=None): diff --git a/reproject/spherical_intersect/core.py b/reproject/spherical_intersect/core.py index c8a0dcaca..fc2e53536 100644 --- a/reproject/spherical_intersect/core.py +++ b/reproject/spherical_intersect/core.py @@ -170,7 +170,7 @@ def parallel_impl(nproc): array_new = sum([_.get()[0] for _ in results]) weights = sum([_.get()[1] for _ in results]) - except KeyboardInterrupt: + except KeyboardInterrupt: # pragma: no cover # If we hit ctrl+c while running things in parallel, we want to terminate # everything and erase the pool before re-raising. Note that since we inited the pool # with the _init_worker function, we disabled catching ctrl+c from the subprocesses. ctrl+c @@ -190,15 +190,6 @@ def parallel_impl(nproc): return array_new / weights, weights if _method == "c" and (nproc is None or nproc > 1): - try: - return parallel_impl(nproc) - except KeyboardInterrupt: - # If we stopped the parallel implementation with ctrl+c, we don't really want to run - # the serial one. - raise - except Exception as e: - warnings.warn("The parallel implementation failed, the reported error message is: '{0}'".format(repr(e,)), AstropyUserWarning) - warnings.warn("Running the serial implementation instead", AstropyUserWarning) - return serial_impl() + return parallel_impl(nproc) raise ValueError('unrecognized method "{0}"'.format(_method,)) diff --git a/reproject/spherical_intersect/high_level.py b/reproject/spherical_intersect/high_level.py index 46f90ef87..2bb58f355 100644 --- a/reproject/spherical_intersect/high_level.py +++ b/reproject/spherical_intersect/high_level.py @@ -56,6 +56,6 @@ def reproject_exact(input_data, output_projection, shape_out=None, hdu_in=None, wcs_out, shape_out = parse_output_projection(output_projection, shape_out=shape_out) if wcs_in.has_celestial and wcs_in.naxis == 2: - return _reproject_celestial(array_in, wcs_in, wcs_out, shape_out=shape_out) + return _reproject_celestial(array_in, wcs_in, wcs_out, shape_out=shape_out, parallel=parallel) else: raise NotImplementedError("Currently only data with a 2-d celestial WCS can be reprojected using flux-conserving algorithm") diff --git a/reproject/spherical_intersect/tests/test_high_level.py b/reproject/spherical_intersect/tests/test_high_level.py new file mode 100644 index 000000000..0bbcfd20d --- /dev/null +++ b/reproject/spherical_intersect/tests/test_high_level.py @@ -0,0 +1,42 @@ +# Licensed under a 3-clause BSD style license - see LICENSE.rst +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +import numpy as np +from astropy.io import fits +from astropy.wcs import WCS +from astropy.utils.data import get_pkg_data_filename +from astropy.tests.helper import pytest + +from ..high_level import reproject_exact + + +class TestReprojectExact(object): + + def setup_class(self): + + self.header_in = fits.Header.fromtextfile(get_pkg_data_filename('../../tests/data/gc_ga.hdr')) + self.header_out = fits.Header.fromtextfile(get_pkg_data_filename('../../tests/data/gc_eq.hdr')) + + self.header_out['NAXIS'] = 2 + self.header_out['NAXIS1'] = 600 + self.header_out['NAXIS2'] = 550 + + self.array_in = np.ones((100, 100)) + + self.wcs_in = WCS(self.header_in) + self.wcs_out = WCS(self.header_out) + + def test_array_wcs(self): + reproject_exact((self.array_in, self.wcs_in), self.wcs_out, shape_out=(200, 200)) + + def test_array_header(self): + reproject_exact((self.array_in, self.header_in), self.header_out) + + def test_parallel_option(self): + + reproject_exact((self.array_in, self.header_in), self.header_out, parallel=1) + + with pytest.raises(ValueError) as exc: + reproject_exact((self.array_in, self.header_in), self.header_out, parallel=-1) + assert exc.value.args[0] == "The number of processors to use must be strictly positive" From 599478a3c3def3d631fdd1d245db4f5a88b6bcea Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 6 May 2015 22:34:48 +0200 Subject: [PATCH 2/3] More improvements to test coverage --- reproject/tests/test_utils.py | 76 +++++++++++++++++++++++++++++++++++ reproject/utils.py | 4 +- 2 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 reproject/tests/test_utils.py diff --git a/reproject/tests/test_utils.py b/reproject/tests/test_utils.py new file mode 100644 index 000000000..0847f08cf --- /dev/null +++ b/reproject/tests/test_utils.py @@ -0,0 +1,76 @@ +import numpy as np + +from astropy.tests.helper import pytest +from astropy.coordinates import FK5, Galactic, ICRS +from astropy.io import fits +from astropy.wcs import WCS +from astropy.utils.data import get_pkg_data_filename + +from ..utils import parse_input_data, parse_output_projection + + +def test_parse_input_data(tmpdir): + + header = fits.Header.fromtextfile(get_pkg_data_filename('data/gc_ga.hdr')) + + data = np.arange(200).reshape((10,20)) + + hdu = fits.ImageHDU(data) + + # As HDU + array, coordinate_system = parse_input_data(hdu) + np.testing.assert_allclose(array, data) + + # As filename + filename = tmpdir.join('test.fits').strpath + hdu.writeto(filename) + + with pytest.raises(ValueError) as exc: + array, coordinate_system = parse_input_data(filename) + assert exc.value.args[0] == "More than one HDU is present, please specify HDU to use with ``hdu_in=`` option" + + array, coordinate_system = parse_input_data(filename, hdu_in=1) + np.testing.assert_allclose(array, data) + + # As array, header + array, coordinate_system = parse_input_data((data, header)) + np.testing.assert_allclose(array, data) + + # As array, WCS + wcs = WCS(hdu.header) + array, coordinate_system = parse_input_data((data, wcs)) + np.testing.assert_allclose(array, data) + + # Invalid + with pytest.raises(TypeError) as exc: + parse_input_data(data) + assert exc.value.args[0] == "input_data should either be an HDU object or a tuple of (array, WCS) or (array, Header)" + + + +def test_parse_output_projection(tmpdir): + + header = fits.Header.fromtextfile(get_pkg_data_filename('data/gc_ga.hdr')) + wcs = WCS(header) + + # As header + + with pytest.raises(ValueError) as exc: + parse_output_projection(header) + assert exc.value.args[0] == "Need to specify shape since output header does not contain complete shape information" + + parse_output_projection(header, shape_out=(200, 200)) + + header['NAXIS'] = 2 + header['NAXIS1'] = 200 + header['NAXIS2'] = 300 + + parse_output_projection(header) + + # As WCS + + with pytest.raises(ValueError) as exc: + parse_output_projection(wcs) + assert exc.value.args[0] == "Need to specify shape when specifying output_projection as WCS object" + + parse_output_projection(wcs, shape_out=(200, 200)) diff --git a/reproject/utils.py b/reproject/utils.py index 5e91580fa..c3fe1cd66 100644 --- a/reproject/utils.py +++ b/reproject/utils.py @@ -12,9 +12,9 @@ def parse_input_data(input_data, hdu_in=None): """ if isinstance(input_data, six.string_types): - return parse_input_data(fits.open(input_data)) + return parse_input_data(fits.open(input_data), hdu_in=hdu_in) elif isinstance(input_data, HDUList): - if len(input_data) > 1: + if len(input_data) > 1 and hdu_in is None: raise ValueError("More than one HDU is present, please specify HDU to use with ``hdu_in=`` option") return parse_input_data(input_data[hdu_in]) elif isinstance(input_data, (PrimaryHDU, ImageHDU, CompImageHDU)): From a7ce73ceea795b33bac641de0895727abd138c35 Mon Sep 17 00:00:00 2001 From: Thomas Robitaille Date: Wed, 6 May 2015 22:42:24 +0200 Subject: [PATCH 3/3] Improve test coverage further --- reproject/healpix/tests/test_healpix.py | 25 ++++++++++++++++++++----- reproject/healpix/tests/test_utils.py | 7 +++++++ 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/reproject/healpix/tests/test_healpix.py b/reproject/healpix/tests/test_healpix.py index 9f6761873..106bbf03b 100644 --- a/reproject/healpix/tests/test_healpix.py +++ b/reproject/healpix/tests/test_healpix.py @@ -10,9 +10,17 @@ from astropy.wcs import WCS from astropy.tests.helper import pytest +try: + import healpy + HAS_HEALPY = True +except: + HAS_HEALPY = False + from ..high_level import reproject_from_healpix, reproject_to_healpix from ...tests.test_high_level import ALL_DTYPES + + DATA = os.path.join(os.path.dirname(__file__), 'data') @@ -37,7 +45,7 @@ def get_reference_header(oversample=2, nside=1): return reference_header -@pytest.mark.importorskip('healpy') +@pytest.mark.skipif('not HAS_HEALPY') @pytest.mark.parametrize("nside,nested,healpix_system,image_system,dtype", itertools.product([1, 2, 4, 8, 16, 32, 64], [True, False], 'C', 'C', ALL_DTYPES)) def test_reproject_healpix_to_image_round_trip( @@ -56,19 +64,26 @@ def test_reproject_healpix_to_image_round_trip( image_data, footprint = reproject_from_healpix( (healpix_data, healpix_system), wcs_out, shape_out=shape_out, - order=0, nested=nested) + order='nearest-neighbor', nested=nested) healpix_data_2, footprint = reproject_to_healpix( (image_data, wcs_out), healpix_system, - nside=nside, order=0, nested=nested) + nside=nside, order='nearest-neighbor', nested=nested) np.testing.assert_array_equal(healpix_data, healpix_data_2) -@pytest.mark.importorskip('healpy') +@pytest.mark.skipif('not HAS_HEALPY') def test_reproject_file(): - reference_header = get_reference_header(oversample=2, nside=8) data, footprint = reproject_from_healpix(os.path.join(DATA, 'bayestar.fits.gz'), reference_header) reference_result = fits.getdata(os.path.join(DATA, 'reference_result.fits')) np.testing.assert_allclose(data, reference_result) + + +@pytest.mark.skipif('not HAS_HEALPY') +def test_reproject_invalid_order(): + reference_header = get_reference_header(oversample=2, nside=8) + with pytest.raises(ValueError) as exc: + reproject_from_healpix(os.path.join(DATA, 'bayestar.fits.gz'), reference_header, order='bicubic') + assert exc.value.args[0] == "Only nearest-neighbor and bilinear interpolation are supported" diff --git a/reproject/healpix/tests/test_utils.py b/reproject/healpix/tests/test_utils.py index 609fd1fdc..06bfca2c9 100644 --- a/reproject/healpix/tests/test_utils.py +++ b/reproject/healpix/tests/test_utils.py @@ -4,6 +4,12 @@ from astropy.coordinates import FK5, Galactic, ICRS from astropy.io import fits +try: + import healpy + HAS_HEALPY = True +except: + HAS_HEALPY = False + from ..utils import parse_coord_system, parse_input_healpix_data def test_parse_coord_system(): @@ -26,6 +32,7 @@ def test_parse_coord_system(): assert exc.value.args[0] == "Could not determine frame for system=spam" +@pytest.mark.skipif('not HAS_HEALPY') def test_parse_input_healpix_data(tmpdir): data = np.arange(3072)