diff --git a/conftest.py b/conftest.py index acbdb0f0765..ac9d2731df7 100644 --- a/conftest.py +++ b/conftest.py @@ -5,5 +5,3 @@ def pytest_addoption(parser): """Add command-line flags for pytest.""" parser.addoption("--run-flaky", action="store_true", help="runs flaky tests") - parser.addoption("--skip-slow", action="store_true", - help="skips slow tests") diff --git a/doc/whats-new.rst b/doc/whats-new.rst index 3d82ed9b466..fa0d1f0d4ba 100644 --- a/doc/whats-new.rst +++ b/doc/whats-new.rst @@ -33,6 +33,11 @@ Bug fixes (:issue:`1359`). `Stephan Hoyer `_. +- Ensure test suite works when runs from an installed version of xarray + (:issue:`1336`). Use ``@pytest.mark.slow`` instead of a custom flag to mark + slow tests. + By `Stephan Hoyer `_ + .. _whats-new.0.9.2: v0.9.2 (2 April, 2017) diff --git a/xarray/tests/__init__.py b/xarray/tests/__init__.py index a88c53ca75f..95af04b9e40 100644 --- a/xarray/tests/__init__.py +++ b/xarray/tests/__init__.py @@ -78,49 +78,36 @@ # slighly simpler construction that the full functions. # Generally `pytest.importorskip('package')` inline is even easier -requires_matplotlib = pytest.mark.skipif(not has_matplotlib, reason='requires matplotlib') +requires_matplotlib = pytest.mark.skipif( + not has_matplotlib, reason='requires matplotlib') +requires_scipy = pytest.mark.skipif( + not has_scipy, reason='requires scipy') +requires_pydap = pytest.mark.skipif( + not has_pydap, reason='requires pydap') +requires_netCDF4 = pytest.mark.skipif( + not has_netCDF4, reason='requires netCDF4') +requires_h5netcdf = pytest.mark.skipif( + not has_h5netcdf, reason='requires h5netcdf') +requires_pynio = pytest.mark.skipif( + not has_pynio, reason='requires pynio') +requires_scipy_or_netCDF4 = pytest.mark.skipif( + not has_scipy and not has_netCDF4, reason='requires scipy or netCDF4') +requires_dask = pytest.mark.skipif( + not has_dask, reason='requires dask') +requires_bottleneck = pytest.mark.skipif( + not has_bottleneck, reason='requires bottleneck') -def requires_scipy(test): - return test if has_scipy else pytest.mark.skip('requires scipy')(test) - - -def requires_pydap(test): - return test if has_pydap else pytest.mark.skip('requires pydap.client')(test) - - -def requires_netCDF4(test): - return test if has_netCDF4 else pytest.mark.skip('requires netCDF4')(test) - - -def requires_h5netcdf(test): - return test if has_h5netcdf else pytest.mark.skip('requires h5netcdf')(test) - - -def requires_pynio(test): - return test if has_pynio else pytest.mark.skip('requires pynio')(test) - - -def requires_scipy_or_netCDF4(test): - return (test if has_scipy or has_netCDF4 - else pytest.mark.skip('requires scipy or netCDF4')(test)) - - -def requires_dask(test): - return test if has_dask else pytest.mark.skip('requires dask')(test) - - -def requires_bottleneck(test): - return test if has_bottleneck else pytest.mark.skip('requires bottleneck')(test) - +try: + _SKIP_FLAKY = not pytest.config.getoption("--run-flaky") +except ValueError: + # Can't get config from pytest, e.g., because xarray is installed instead + # of being run from a development version (and hence conftests.py is not + # available). Don't run flaky tests. + _SKIP_FLAKY = True flaky = pytest.mark.skipif( - not pytest.config.getoption("--run-flaky"), - reason="set --run-flaky option to run flaky tests") - -slow = pytest.mark.skipif( - pytest.config.getoption("--skip-slow"), - reason="set --skip-slow option to run slow tests") + _SKIP_FLAKY, reason="set --run-flaky option to run flaky tests") class TestCase(unittest.TestCase): diff --git a/xarray/tests/test_backends.py b/xarray/tests/test_backends.py index e7a2dbd4381..d417fde4dda 100644 --- a/xarray/tests/test_backends.py +++ b/xarray/tests/test_backends.py @@ -27,7 +27,7 @@ from . import (TestCase, requires_scipy, requires_netCDF4, requires_pydap, requires_scipy_or_netCDF4, requires_dask, requires_h5netcdf, requires_pynio, has_netCDF4, has_scipy, assert_allclose, - flaky, slow) + flaky) from .test_dataset import create_test_data try: @@ -1118,21 +1118,21 @@ def test_4_autoclose_h5netcdf(self): @requires_dask @requires_netCDF4 @flaky - @slow + @pytest.mark.slow def test_1_open_large_num_files_netcdf4(self): self.validate_open_mfdataset_large_num_files(engine=['netcdf4']) @requires_dask @requires_scipy @flaky - @slow + @pytest.mark.slow def test_2_open_large_num_files_scipy(self): self.validate_open_mfdataset_large_num_files(engine=['scipy']) @requires_dask @requires_pynio @flaky - @slow + @pytest.mark.slow def test_3_open_large_num_files_pynio(self): self.validate_open_mfdataset_large_num_files(engine=['pynio']) @@ -1142,7 +1142,7 @@ def test_3_open_large_num_files_pynio(self): @requires_h5netcdf @flaky @pytest.mark.xfail - @slow + @pytest.mark.slow def test_4_open_large_num_files_h5netcdf(self): self.validate_open_mfdataset_large_num_files(engine=['h5netcdf']) diff --git a/xarray/tests/test_dataset.py b/xarray/tests/test_dataset.py index f9a3568b1ca..ec1e041deb1 100644 --- a/xarray/tests/test_dataset.py +++ b/xarray/tests/test_dataset.py @@ -3342,21 +3342,21 @@ def test_dir_unicode(data_set): @pytest.fixture(params=[1]) def ds(request): if request.param == 1: - return Dataset({'z1': (['y', 'x'], np.random.randn(5, 20)), - 'z2': (['time', 'y'], np.random.randn(30, 5))}, - {'x': ('x', np.linspace(0, 1.0, 20)), - 'time': ('time', np.linspace(0, 1.0, 30)), - 'c': ('y', ['a', 'b', 'c', 'd', 'e']), - 'y': range(5)}) + return Dataset({'z1': (['y', 'x'], np.random.randn(2, 8)), + 'z2': (['time', 'y'], np.random.randn(10, 2))}, + {'x': ('x', np.linspace(0, 1.0, 8)), + 'time': ('time', np.linspace(0, 1.0, 10)), + 'c': ('y', ['a', 'b']), + 'y': range(2)}) if request.param == 2: - return Dataset({'z1': (['time', 'y'], np.random.randn(30, 5)), - 'z2': (['time'], np.random.randn(30)), - 'z3': (['x', 'time'], np.random.randn(20, 30))}, - {'x': ('x', np.linspace(0, 1.0, 20)), - 'time': ('time', np.linspace(0, 1.0, 30)), - 'c': ('y', ['a', 'b', 'c', 'd', 'e']), - 'y': range(5)}) + return Dataset({'z1': (['time', 'y'], np.random.randn(10, 2)), + 'z2': (['time'], np.random.randn(10)), + 'z3': (['x', 'time'], np.random.randn(8, 10))}, + {'x': ('x', np.linspace(0, 1.0, 8)), + 'time': ('time', np.linspace(0, 1.0, 10)), + 'c': ('y', ['a', 'b']), + 'y': range(2)}) def test_rolling_properties(ds): @@ -3440,6 +3440,7 @@ def test_rolling_pandas_compat(center, window, min_periods): ds_rolling['index']) +@pytest.mark.slow @pytest.mark.parametrize('ds', (1, 2), indirect=True) @pytest.mark.parametrize('center', (True, False)) @pytest.mark.parametrize('min_periods', (None, 1, 2, 3)) diff --git a/xarray/tests/test_plot.py b/xarray/tests/test_plot.py index 621c1680492..cba03bd8361 100644 --- a/xarray/tests/test_plot.py +++ b/xarray/tests/test_plot.py @@ -17,6 +17,7 @@ import numpy as np import pandas as pd from datetime import datetime +import pytest from xarray import DataArray @@ -26,9 +27,11 @@ _build_discrete_cmap, _color_palette) -from . import TestCase, requires_matplotlib, slow +from . import TestCase, requires_matplotlib +@pytest.mark.flaky +@pytest.mark.skip(reason='maybe flaky') def text_in_fig(): ''' Return the set of all text in the figure @@ -74,7 +77,7 @@ def pass_in_axis(self, plotmethod): plotmethod(ax=axes[0]) self.assertTrue(axes[0].has_data()) - @slow + @pytest.mark.slow def imshow_called(self, plotmethod): plotmethod() images = plt.gca().findobj(mpl.image.AxesImage) @@ -101,7 +104,7 @@ def test_2d_before_squeeze(self): def test2d_uniform_calls_imshow(self): self.assertTrue(self.imshow_called(self.darray[:, :, 0].plot.imshow)) - @slow + @pytest.mark.slow def test2d_nonuniform_calls_contourf(self): a = self.darray[:, :, 0] a.coords['dim_1'] = [2, 1, 89] @@ -117,8 +120,9 @@ def test__infer_interval_breaks(self): self.assertArrayEqual([-0.5, 0.5, 1.5], _infer_interval_breaks([0, 1])) self.assertArrayEqual([-0.5, 0.5, 5.0, 9.5, 10.5], _infer_interval_breaks([0, 1, 9, 10])) - self.assertArrayEqual(pd.date_range('20000101', periods=4) - np.timedelta64(12, 'h'), - _infer_interval_breaks(pd.date_range('20000101', periods=3))) + self.assertArrayEqual( + pd.date_range('20000101', periods=4) - np.timedelta64(12, 'h'), + _infer_interval_breaks(pd.date_range('20000101', periods=3))) # make a bounded 2D array that we will center and re-infer xref, yref = np.meshgrid(np.arange(6), np.arange(5)) @@ -141,7 +145,7 @@ def test_datetime_dimension(self): ax = plt.gca() self.assertTrue(ax.has_data()) - @slow + @pytest.mark.slow def test_convenient_facetgrid(self): a = easy_array((10, 15, 4)) d = DataArray(a, dims=['y', 'x', 'z']) @@ -158,7 +162,7 @@ def test_convenient_facetgrid(self): with self.assertRaisesRegexp(ValueError, '[Ff]acet'): d[0].plot(x='x', y='y', col='z', ax=plt.gca()) - @slow + @pytest.mark.slow def test_subplot_kws(self): a = easy_array((10, 15, 4)) d = DataArray(a, dims=['y', 'x', 'z']) @@ -173,7 +177,7 @@ def test_subplot_kws(self): except AttributeError: self.assertEqual(ax.get_axis_bgcolor(), 'r') - @slow + @pytest.mark.slow def test_plot_size(self): self.darray[:, 0, 0].plot(figsize=(13, 5)) assert tuple(plt.gcf().get_size_inches()) == (13, 5) @@ -199,7 +203,7 @@ def test_plot_size(self): with self.assertRaisesRegexp(ValueError, 'cannot provide `aspect`'): self.darray.plot(aspect=1) - @slow + @pytest.mark.slow def test_convenient_facetgrid_4d(self): a = easy_array((10, 15, 2, 3)) d = DataArray(a, dims=['y', 'x', 'columns', 'rows']) @@ -254,7 +258,7 @@ def test_primitive_returned(self): p = self.darray.plot.line() self.assertTrue(isinstance(p[0], mpl.lines.Line2D)) - @slow + @pytest.mark.slow def test_plot_nans(self): self.darray[1] = np.nan self.darray.plot.line() @@ -306,7 +310,7 @@ def test_primitive_returned(self): h = self.darray.plot.hist() self.assertTrue(isinstance(h[-1][0], mpl.patches.Rectangle)) - @slow + @pytest.mark.slow def test_plot_nans(self): self.darray[0, 0, 0] = np.nan self.darray.plot.hist() @@ -335,7 +339,7 @@ def test_center(self): self.assertIsNone(cmap_params['levels']) self.assertIsNone(cmap_params['norm']) - @slow + @pytest.mark.slow def test_integer_levels(self): data = self.data + 1 @@ -475,13 +479,13 @@ def setUp(self): self.data_min = distance.min() self.data_max = distance.max() - @slow + @pytest.mark.slow def test_recover_from_seaborn_jet_exception(self): pal = _color_palette('jet', 4) self.assertTrue(type(pal) == np.ndarray) self.assertEqual(len(pal), 4) - @slow + @pytest.mark.slow def test_build_discrete_cmap(self): for (cmap, levels, extend, filled) in [('jet', [0, 1], 'both', False), ('hot', [-4, 4], 'max', True)]: @@ -497,7 +501,7 @@ def test_build_discrete_cmap(self): else: self.assertEqual(ncmap.colorbar_extend, 'max') - @slow + @pytest.mark.slow def test_discrete_colormap_list_of_levels(self): for extend, levels in [('max', [-1, 2, 4, 8, 10]), ('both', [2, 5, 10, 11]), @@ -514,7 +518,7 @@ def test_discrete_colormap_list_of_levels(self): self.assertEqual('max', primitive.cmap.colorbar_extend) self.assertEqual(len(levels) - 1, len(primitive.cmap.colors)) - @slow + @pytest.mark.slow def test_discrete_colormap_int_levels(self): for extend, levels, vmin, vmax in [('neither', 7, None, None), ('neither', 7, None, 20), @@ -821,17 +825,17 @@ def test_facetgrid_cmap(self): d = DataArray(data, dims=['x', 'y', 'time']) fg = d.plot.pcolormesh(col='time') # check that all color limits are the same - self.assertTrue(len(set(m.get_clim() for m in fg._mappables)) == 1) + self.assertEqual(len(set(m.get_clim() for m in fg._mappables)), 1) # check that all colormaps are the same - self.assertTrue(len(set(m.get_cmap().name for m in fg._mappables)) == 1) + self.assertEqual(len(set(m.get_cmap().name for m in fg._mappables)), 1) -@slow +@pytest.mark.slow class TestContourf(Common2dMixin, PlotTestCase): plotfunc = staticmethod(xplt.contourf) - @slow + @pytest.mark.slow def test_contourf_called(self): # Having both statements ensures the test works properly self.assertFalse(self.contourf_called(self.darray.plot.imshow)) @@ -841,7 +845,7 @@ def test_primitive_artist_returned(self): artist = self.plotmethod() self.assertTrue(isinstance(artist, mpl.contour.QuadContourSet)) - @slow + @pytest.mark.slow def test_extend(self): artist = self.plotmethod() self.assertEqual(artist.extend, 'neither') @@ -859,7 +863,7 @@ def test_extend(self): artist = self.plotmethod(vmin=-10, vmax=0) self.assertEqual(artist.extend, 'max') - @slow + @pytest.mark.slow def test_2d_coord_names(self): self.plotmethod(x='x2d', y='y2d') # make sure labels came out ok @@ -867,7 +871,7 @@ def test_2d_coord_names(self): self.assertEqual('x2d', ax.get_xlabel()) self.assertEqual('y2d', ax.get_ylabel()) - @slow + @pytest.mark.slow def test_levels(self): artist = self.plotmethod(levels=[-0.5, -0.4, 0.1]) self.assertEqual(artist.extend, 'both') @@ -876,7 +880,7 @@ def test_levels(self): self.assertEqual(artist.extend, 'neither') -@slow +@pytest.mark.slow class TestContour(Common2dMixin, PlotTestCase): plotfunc = staticmethod(xplt.contour) @@ -906,7 +910,7 @@ def _color_as_tuple(c): (1.0, 1.0, 1.0)) # the last color is now under "over" self.assertEqual( - _color_as_tuple(artist.cmap._rgba_over), + _color_as_tuple(artist.cmap._rgba_over), (0.0, 0.0, 1.0)) def test_cmap_and_color_both(self): @@ -917,7 +921,7 @@ def list_of_colors_in_cmap_deprecated(self): with self.assertRaises(Exception): self.plotmethod(cmap=['k', 'b']) - @slow + @pytest.mark.slow def test_2d_coord_names(self): self.plotmethod(x='x2d', y='y2d') # make sure labels came out ok @@ -944,7 +948,7 @@ def test_everything_plotted(self): artist = self.plotmethod() self.assertEqual(artist.get_array().size, self.darray.size) - @slow + @pytest.mark.slow def test_2d_coord_names(self): self.plotmethod(x='x2d', y='y2d') # make sure labels came out ok @@ -962,12 +966,13 @@ def test_dont_infer_interval_breaks_for_cartopy(self): # Let cartopy handle the axis limits and artist size self.assertTrue(artist.get_array().size <= self.darray.size) -@slow + +@pytest.mark.slow class TestImshow(Common2dMixin, PlotTestCase): plotfunc = staticmethod(xplt.imshow) - @slow + @pytest.mark.slow def test_imshow_called(self): # Having both statements ensures the test works properly self.assertFalse(self.imshow_called(self.darray.plot.contourf)) @@ -982,7 +987,7 @@ def test_default_aspect_is_auto(self): self.darray.plot.imshow() self.assertEqual('auto', plt.gca().get_aspect()) - @slow + @pytest.mark.slow def test_cannot_change_mpl_aspect(self): with self.assertRaisesRegexp(ValueError, 'not available in xarray'): @@ -993,15 +998,15 @@ def test_cannot_change_mpl_aspect(self): self.assertEqual('auto', plt.gca().get_aspect()) assert tuple(plt.gcf().get_size_inches()) == (10, 5) - @slow + @pytest.mark.slow def test_primitive_artist_returned(self): artist = self.plotmethod() self.assertTrue(isinstance(artist, mpl.image.AxesImage)) - @slow + @pytest.mark.slow def test_seaborn_palette_needs_levels(self): try: - import seaborn + import seaborn # noqa with self.assertRaises(ValueError): self.plotmethod(cmap='husl') except ImportError: @@ -1011,6 +1016,7 @@ def test_2d_coord_names(self): with self.assertRaisesRegexp(ValueError, 'requires 1D coordinates'): self.plotmethod(x='x2d', y='y2d') + class TestFacetGrid(PlotTestCase): def setUp(self): @@ -1019,7 +1025,7 @@ def setUp(self): coords={'z': ['a', 'b', 'c']}) self.g = xplt.FacetGrid(self.darray, col='z') - @slow + @pytest.mark.slow def test_no_args(self): self.g.map_dataarray(xplt.contourf, 'x', 'y') @@ -1034,7 +1040,7 @@ def test_no_args(self): fontsize = ax.title.get_size() self.assertLessEqual(fontsize, 12) - @slow + @pytest.mark.slow def test_names_appear_somewhere(self): self.darray.name = 'testvar' self.g.map_dataarray(xplt.contourf, 'x', 'y') @@ -1046,7 +1052,7 @@ def test_names_appear_somewhere(self): for label in ['x', 'y']: self.assertIn(label, alltxt) - @slow + @pytest.mark.slow def test_text_not_super_long(self): self.darray.coords['z'] = [100 * letter for letter in 'abc'] g = xplt.FacetGrid(self.darray, col='z') @@ -1058,7 +1064,7 @@ def test_text_not_super_long(self): t0 = g.axes[0, 0].get_title() self.assertTrue(t0.endswith('...')) - @slow + @pytest.mark.slow def test_colorbar(self): vmin = self.darray.values.min() vmax = self.darray.values.max() @@ -1072,7 +1078,7 @@ def test_colorbar(self): self.assertEqual(1, len(find_possible_colorbars())) - @slow + @pytest.mark.slow def test_empty_cell(self): g = xplt.FacetGrid(self.darray, col='z', col_wrap=2) g.map_dataarray(xplt.imshow, 'x', 'y') @@ -1081,12 +1087,12 @@ def test_empty_cell(self): self.assertFalse(bottomright.has_data()) self.assertFalse(bottomright.get_visible()) - @slow + @pytest.mark.slow def test_norow_nocol_error(self): with self.assertRaisesRegexp(ValueError, r'[Rr]ow'): xplt.FacetGrid(self.darray) - @slow + @pytest.mark.slow def test_groups(self): self.g.map_dataarray(xplt.imshow, 'x', 'y') upperleft_dict = self.g.name_dicts[0, 0] @@ -1095,19 +1101,19 @@ def test_groups(self): self.assertDataArrayEqual(upperleft_array, z0) - @slow + @pytest.mark.slow def test_float_index(self): self.darray.coords['z'] = [0.1, 0.2, 0.4] g = xplt.FacetGrid(self.darray, col='z') g.map_dataarray(xplt.imshow, 'x', 'y') - @slow + @pytest.mark.slow def test_nonunique_index_error(self): self.darray.coords['z'] = [0.1, 0.2, 0.2] with self.assertRaisesRegexp(ValueError, r'[Uu]nique'): xplt.FacetGrid(self.darray, col='z') - @slow + @pytest.mark.slow def test_robust(self): z = np.zeros((20, 20, 2)) darray = DataArray(z, dims=['y', 'x', 'z']) @@ -1129,7 +1135,7 @@ def test_robust(self): largest = max(abs(x) for x in numbers) self.assertLess(largest, 21) - @slow + @pytest.mark.slow def test_can_set_vmin_vmax(self): vmin, vmax = 50.0, 1000.0 expected = np.array((vmin, vmax)) @@ -1139,14 +1145,14 @@ def test_can_set_vmin_vmax(self): clim = np.array(image.get_clim()) self.assertTrue(np.allclose(expected, clim)) - @slow + @pytest.mark.slow def test_can_set_norm(self): norm = mpl.colors.SymLogNorm(0.1) self.g.map_dataarray(xplt.imshow, 'x', 'y', norm=norm) for image in plt.gcf().findobj(mpl.image.AxesImage): self.assertIs(image.norm, norm) - @slow + @pytest.mark.slow def test_figure_size(self): self.assertArrayEqual(self.g.fig.get_size_inches(), (10, 3)) @@ -1169,7 +1175,7 @@ def test_figure_size(self): with self.assertRaisesRegexp(ValueError, "Can't use"): g = xplt.plot(self.darray, row=2, col='z', ax=plt.gca(), size=6) - @slow + @pytest.mark.slow def test_num_ticks(self): nticks = 99 maxticks = nticks + 1 @@ -1184,12 +1190,12 @@ def test_num_ticks(self): self.assertGreaterEqual(xticks, nticks / 2.0) self.assertGreaterEqual(yticks, nticks / 2.0) - @slow + @pytest.mark.slow def test_map(self): self.g.map(plt.contourf, 'x', 'y', Ellipsis) self.g.map(lambda: None) - @slow + @pytest.mark.slow def test_map_dataset(self): g = xplt.FacetGrid(self.darray.to_dataset(name='foo'), col='z') g.map(plt.contourf, 'x', 'y', 'foo') @@ -1208,7 +1214,7 @@ def test_map_dataset(self): self.assertIn('colors!', text_in_fig()) self.assertEqual(1, len(find_possible_colorbars())) - @slow + @pytest.mark.slow def test_set_axis_labels(self): g = self.g.map_dataarray(xplt.contourf, 'x', 'y') g.set_axis_labels('longitude', 'latitude') @@ -1216,7 +1222,7 @@ def test_set_axis_labels(self): for label in ['longitude', 'latitude']: self.assertIn(label, alltxt) - @slow + @pytest.mark.slow def test_facetgrid_colorbar(self): a = easy_array((10, 15, 4)) d = DataArray(a, dims=['y', 'x', 'z'], name='foo') @@ -1230,7 +1236,7 @@ def test_facetgrid_colorbar(self): d.plot.imshow(x='x', y='y', col='z', add_colorbar=False) self.assertEqual(0, len(find_possible_colorbars())) - @slow + @pytest.mark.slow def test_facetgrid_polar(self): # test if polar projection in FacetGrid does not raise an exception self.darray.plot.pcolormesh(col='z', @@ -1250,7 +1256,7 @@ def setUp(self): self.darray = darray - @slow + @pytest.mark.slow def test_default_labels(self): g = xplt.FacetGrid(self.darray, col='col', row='row') self.assertEqual((2, 3), g.axes.shape)