From a60a5bed8317bdfd2a98161bb9f7f1e0f6689a1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Davide=20Sandon=C3=A0?= Date: Tue, 2 Apr 2024 09:28:05 +0200 Subject: [PATCH] Improved test coverage At the same time reduced tests duplicates. --- tests/backends/make_tests.py | 556 +++------ tests/backends/test_base_backend.py | 13 + tests/backends/test_bokeh.py | 538 ++++----- tests/backends/test_k3d.py | 436 +++----- tests/backends/test_matplotlib.py | 1607 +++++++++++---------------- tests/backends/test_plotly.py | 933 +++++----------- tests/conftest.py | 10 + 7 files changed, 1475 insertions(+), 2618 deletions(-) diff --git a/tests/backends/make_tests.py b/tests/backends/make_tests.py index 07045e84..c16b3e7a 100644 --- a/tests/backends/make_tests.py +++ b/tests/backends/make_tests.py @@ -5,7 +5,7 @@ plot3d_implicit, plot3d_parametric_surface, plot_vector, plot_complex, plot_real_imag, plot_riemann_sphere, graphics, arrow_2d, arrow_3d, plot_root_locus, plot_pole_zero, - ngrid, sgrid, zgrid, mcircles + ngrid, sgrid, zgrid, mcircles, surface, surface_parametric, line ) from spb.series import ( SurfaceOver2DRangeSeries, ParametricSurfaceSeries, LineOver1DRangeSeries, @@ -53,65 +53,59 @@ def custom_colorloop_2(B, show=False): ) -def make_plot_1(B, rendering_kw, use_latex=False): +def make_test_plot(B, rendering_kw, use_latex=False): return plot( - sin(x), - cos(x), + sin(a * x), + cos(b * x), rendering_kw=rendering_kw, backend=B, legend=True, use_latex=use_latex, + params={a: (1, 0, 2), b: (1, 0, 2)}, **options() ) -def make_plot_parametric_1(B, rendering_kw): +def make_test_plot_parametric(B, use_cm, rendering_kw={}): return plot_parametric( - cos(x), sin(x), + cos(a * x), sin(b * x), (x, 0, 1.5 * pi), + use_cm=use_cm, backend=B, rendering_kw=rendering_kw, use_latex=False, + params={a: (1, 0, 2), b: (1, 0, 2)}, **options() ) -def make_plot3d_parametric_line_1(B, rendering_kw, show=False): +def make_test_plot3d_parametric_line(B, rendering_kw, use_latex, use_cm, show=False): opts = options() opts["show"] = show return plot3d_parametric_line( - cos(x), sin(x), x, + cos(a * x), sin(b * x), x, (x, -pi, pi), backend=B, rendering_kw=rendering_kw, - use_latex=False, + use_latex=use_latex, + use_cm=use_cm, + params={a: (1, 0, 2), b: (1, 0, 2)}, **opts ) -def make_plot3d_1(B, rendering_kw, show=False): +def make_test_plot3d(B, rendering_kw, use_cm, use_latex, show=False): opts = options() opts["show"] = show return plot3d( - cos(x**2 + y**2), + cos(a * x**2 + y**2), + sin(b * x**2 + y**2), (x, -3, 3), (y, -3, 3), - use_cm=False, + use_latex=use_latex, + use_cm=use_cm, backend=B, rendering_kw=rendering_kw, - **opts - ) - - -def make_plot3d_2(B, show=False): - opts = options() - opts["show"] = show - return plot3d( - cos(x**2 + y**2), - sin(x**2 + y**2), - (x, -3, 3), (y, -3, 3), - use_cm=True, - backend=B, - use_latex=False, + params={a: (1, 0, 2), b: (1, 0, 2)}, **opts ) @@ -223,12 +217,13 @@ def make_plot3d_parametric_surface_wireframe_2(B, wf): ) -def make_plot_contour_1(B, rendering_kw): +def make_test_plot_contour(B, rendering_kw, use_latex): return plot_contour( - cos(x**2 + y**2), (x, -3, 3), (y, -3, 3), + cos(a * x**2 + y**2), (x, -3, 3), (y, -3, 3), backend=B, rendering_kw=rendering_kw, - use_latex=False, + use_latex=use_latex, + params={a: (1, 0, 2)}, **options() ) @@ -243,92 +238,56 @@ def make_plot_contour_is_filled(B, is_filled): ) -def make_plot_vector_2d_quiver(B, contour_kw, quiver_kw): +def make_test_plot_vector_2d_quiver(B, contour_kw, quiver_kw): return plot_vector( - Matrix([x, y]), + Matrix([a * x, y]), (x, -5, 5), (y, -4, 4), backend=B, quiver_kw=quiver_kw, contour_kw=contour_kw, use_latex=False, + params={a: (1, 0, 2)}, **options() ) -def make_plot_vector_2d_streamlines_1(B, stream_kw, contour_kw): - return plot_vector( - Matrix([x, y]), - (x, -5, 5), (y, -4, 4), - backend=B, - stream_kw=stream_kw, - contour_kw=contour_kw, - scalar=(x + y), - streamlines=True, - use_latex=False, - **options() - ) - - -def make_plot_vector_2d_streamlines_2(B, stream_kw, contour_kw): +def make_test_plot_vector_2d_streamlines( + B, stream_kw, contour_kw, scalar, use_latex=False +): return plot_vector( - Matrix([x, y]), + Matrix([a * x, y]), (x, -5, 5), (y, -4, 4), backend=B, stream_kw=stream_kw, contour_kw=contour_kw, - scalar=[(x + y), "test"], + scalar=scalar, streamlines=True, - use_latex=False, + use_latex=use_latex, + params={a: (1, 0, 2)}, **options() ) -def make_plot_vector_3d_quiver(B, quiver_kw, show=False, **kwargs): +def make_test_plot_vector_3d_quiver_streamlines( + B, streamlines, quiver_kw={}, stream_kw={}, show=False, **kwargs +): opts = options() opts["show"] = show opts.pop("adaptive") return plot_vector( - Matrix([z, y, x]), + Matrix([a * z, y, x]), (x, -5, 5), (y, -4, 4), (z, -3, 3), backend=B, quiver_kw=quiver_kw, - use_latex=False, - **opts, - **kwargs - ) - - -def make_plot_vector_3d_streamlines_1(B, stream_kw, show=False, kwargs=dict()): - opts = options() - opts["show"] = show - opts.pop("adaptive") - return plot_vector( - Matrix([z, y, x]), - (x, -5, 5), (y, -4, 4), (z, -3, 3), - backend=B, stream_kw=stream_kw, - streamlines=True, - use_latex=False, + streamlines=streamlines, + params={a: (1, 0, 2)}, **opts, **kwargs ) -def make_plot_vector_2d_normalize_1(B, norm): - opts = options() - opts.pop("adaptive") - return plot_vector( - [-sin(y), cos(x)], - (x, -2, 2), (y, -2, 2), - backend=B, - normalize=norm, - scalar=False, - use_cm=False, - **opts - ) - - -def make_plot_vector_2d_normalize_2(B, norm): +def make_test_plot_vector_2d_normalize(B, norm): opts = options() opts.pop("adaptive") return plot_vector( @@ -343,20 +302,7 @@ def make_plot_vector_2d_normalize_2(B, norm): ) -def make_plot_vector_3d_normalize_1(B, norm): - opts = options() - opts.pop("adaptive") - return plot_vector( - [z, -x, y], - (x, -2, 2), (y, -2, 2), (z, -2, 2), - backend=B, - normalize=norm, - use_cm=False, - **opts - ) - - -def make_plot_vector_3d_normalize_2(B, norm): +def make_test_plot_vector_3d_normalize(B, norm): opts = options() opts.pop("adaptive") return plot_vector( @@ -370,32 +316,21 @@ def make_plot_vector_3d_normalize_2(B, norm): ) -def make_plot_vector_2d_quiver_color_func_1(B, cf): - opts = options() - opts.pop("adaptive") +def make_test_plot_vector_2d_color_func(B, streamlines, cf): return plot_vector( - (-y, x), (x, -2, 2), (y, -2, 2), + (-a * y, x), (x, -2, 2), (y, -2, 2), scalar=False, + streamlines=streamlines, use_cm=True, color_func=cf, + show=False, backend=B, - **opts - ) - - -def make_plot_vector_3d_quiver_color_func_1(B, cf): - opts = options() - opts.pop("adaptive") - return plot_vector( - Matrix([z, y, x]), - (x, -2, 2), (y, -2, 2), (z, -2, 2), - backend=B, - color_func=cf, - **opts + n=3, + params={a: (1, 0, 2)}, ) -def make_plot_vector_3d_quiver_color_func_2(B, cf): +def make_test_plot_vector_3d_quiver_color_func(B, cf): opts = options() opts.pop("adaptive") return plot_vector( @@ -408,16 +343,17 @@ def make_plot_vector_3d_quiver_color_func_2(B, cf): ) -def make_plot_vector_3d_streamlines_color_func(B, cf): +def make_test_plot_vector_3d_streamlines_color_func(B, cf): # NOTE: better keep a decent number of discretization points in order to # be sure to have streamlines return plot_vector( - Matrix([z, y, x]), + Matrix([a*z, y, x]), (x, -2, 2), (y, -2, 2), (z, -2, 2), streamlines=True, show=False, backend=B, color_func=cf, + params={a: (1, 0, 2)}, n=7, ) @@ -445,25 +381,25 @@ def make_test_plot_implicit_adaptive_false(B, rendering_kw): ) -def make_test_real_imag(B, rendering_kw): +def make_test_real_imag(B, rendering_kw, use_latex): return plot_real_imag( sqrt(x), (x, -5, 5), backend=B, rendering_kw=rendering_kw, - use_latex=False, + use_latex=use_latex, **options() ) -def make_test_plot_complex_1d(B, rendering_kw): +def make_test_plot_complex_1d(B, rendering_kw, use_latex): return plot_complex( sqrt(x), (x, -5, 5), - backend=B, rendering_kw=rendering_kw, + backend=B, rendering_kw=rendering_kw, use_latex=use_latex, **options() ) -def make_test_plot_complex_2d(B, rendering_kw): +def make_test_plot_complex_2d(B, rendering_kw, use_latex=False): opts = options() opts.pop("adaptive") return plot_complex( @@ -471,6 +407,7 @@ def make_test_plot_complex_2d(B, rendering_kw): backend=B, coloring="a", rendering_kw=rendering_kw, + use_latex=use_latex, **opts ) @@ -488,25 +425,13 @@ def make_test_plot_complex_3d(B, rendering_kw): ) -def make_test_plot_list_is_filled_false(B): - return plot_list( - [1, 2, 3], - [1, 2, 3], - backend=B, - is_point=True, - is_filled=False, - show=False, - use_latex=False, - ) - - -def make_test_plot_list_is_filled_true(B): +def make_test_plot_list_is_filled(B, is_filled): return plot_list( [1, 2, 3], [1, 2, 3], backend=B, is_point=True, - is_filled=True, + is_filled=is_filled, show=False, use_latex=False, ) @@ -588,18 +513,6 @@ def make_test_plot_geometry_3d(B): ) -def make_test_vectors_3d_update_interactive(B): - return plot_vector( - [a * z, b * y, c * x], - (x, -5, 5), (y, -5, 5), (z, -5, 5), - params={a: (1, 0, 2), b: (1, 0, 2), c: (1, 0, 2)}, - streamlines=True, - n=5, - backend=B, - show=False, - ) - - def make_test_aspect_ratio_2d_issue_11764(B, aspect="auto"): return plot_parametric( cos(x), sin(x), (x, 0, 2 * pi), backend=B, aspect=aspect, **options() @@ -646,163 +559,6 @@ def make_test_backend_latex_labels_2(B, use_latex, show=False): ) -def make_test_plot_use_latex(B): - return plot( - sin(x), cos(x), - backend=B, legend=True, use_latex=True, - **options() - ) - - -def make_test_plot_parametric_use_latex(B): - return plot_parametric( - cos(x), sin(x), (x, 0, 1.5 * pi), - backend=B, use_latex=True, - **options() - ) - - -def make_test_plot_contour_use_latex(B): - return plot_contour( - cos(x**2 + y**2), - (x, -3, 3), (y, -3, 3), - backend=B, - use_latex=True, - **options() - ) - - -def make_test_plot3d_parametric_line_use_latex(B, show=False): - opts = options() - opts["show"] = show - return plot3d_parametric_line( - cos(x), sin(x), x, (x, -pi, pi), backend=B, use_latex=True, **opts - ) - - -def make_test_plot3d_use_latex(B, show=False): - opts = options() - opts["show"] = show - return plot3d( - cos(x**2 + y**2), - sin(x**2 + y**2), - (x, -3, 3), (y, -3, 3), - use_cm=True, - backend=B, - use_latex=True, - **opts - ) - - -def make_test_plot_vector_2d_quivers_use_latex(B): - return plot_vector( - Matrix([x, y]), (x, -5, 5), (y, -4, 4), - backend=B, **options() - ) - - -def make_test_plot_vector_2d_streamlines_custom_scalar_field_use_latex(B): - return plot_vector( - Matrix([x, y]), - (x, -5, 5), (y, -4, 4), - backend=B, - scalar=(x + y), - streamlines=True, - use_latex=True, - **options() - ) - - -def make_test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex(B): - return plot_vector( - Matrix([x, y]), - (x, -5, 5), (y, -4, 4), - backend=B, - scalar=[(x + y), "test"], - streamlines=True, - use_latex=True, - **options() - ) - - -def make_test_plot_vector_2d_use_latex_colorbar(B, scalar, streamlines): - opts = options() - opts.pop("adaptive") - return plot_vector( - Matrix([x, y]), - (x, -5, 5), (y, -4, 4), - backend=B, - scalar=scalar, - streamlines=streamlines, - use_cm=True, - use_latex=True, - **opts - ) - - -def make_test_plot_vector_3d_quivers_use_latex(B, show=False): - opts = options() - opts["show"] = show - opts.pop("adaptive") - return plot_vector( - Matrix([z, y, x]), - (x, -5, 5), (y, -4, 4), (z, -3, 3), - backend=B, - use_cm=True, - use_latex=True, - **opts - ) - - -def make_test_plot_vector_3d_streamlines_use_latex(B, show=False): - opts = options() - opts["show"] = show - opts.pop("adaptive") - return plot_vector( - Matrix([z, y, x]), - (x, -5, 5), (y, -4, 4), (z, -3, 3), - backend=B, - streamlines=True, - use_latex=True, - **opts - ) - - -def make_test_plot_complex_use_latex_1(B): - return plot_complex( - cos(x) + sin(I * x), (x, -2, 2), - use_latex=True, backend=B, - **options() - ) - - -def make_test_plot_complex_use_latex_2(B): - opts = options() - opts.pop("adaptive") - return plot_complex( - gamma(z), (z, -3 - 3 * I, 3 + 3 * I), - use_latex=True, backend=B, - **opts - ) - - -def make_test_plot_real_imag_use_latex(B): - return plot_real_imag( - sqrt(x), (x, -3, 3), - backend=B, use_latex=True, - **options() - ) - - -def make_test_plot3d_use_cm(B, use_cm, show=False): - opts = options() - opts["show"] = show - return plot3d( - cos(x**2 + y**2), (x, -1, 1), (y, -1, 1), - backend=B, use_cm=use_cm, **opts - ) - - def make_test_plot_polar(B, pa=False): return plot_polar( 1 + sin(10 * x) / 10, (x, 0, 2 * pi), @@ -836,87 +592,50 @@ def make_test_plot3d_implicit(B, show=False): ) -def make_test_surface_color_func_1(B, col, show=False): - opts = options() - opts["show"] = show - return plot3d( - cos(x**2 + y**2), (x, -3, 3), (y, -3, 3), - backend=B, - color_func=col, - use_cm=True, - **opts - ) - - -def make_test_surface_color_func_2(B, col, show=False): - opts = options() - opts["show"] = show - opts.pop("adaptive") - r = 2 + sin(7 * u + 5 * v) - expr = (r * cos(u) * sin(v), r * sin(u) * sin(v), r * cos(v)) - return plot3d_parametric_surface( - *expr, (u, 0, 2 * pi), (v, 0, pi), - use_cm=True, - backend=B, - color_func=col, - **opts - ) - - -def make_test_surface_interactive_color_func(B): +def make_test_surface_color_func(B): expr1 = t * cos(x**2 + y**2) r = 2 + sin(7 * u + 5 * v) expr2 = (t * r * cos(u) * sin(v), r * sin(u) * sin(v), r * cos(v)) + params = {t: (1, 0, 2)} - s1 = SurfaceOver2DRangeSeries( - expr1, (x, -5, 5), (y, -5, 5), - n1=5, n2=5, - params={t: 1}, - use_cm=True, - color_func=lambda x, y, z: z, - ) - s2 = SurfaceOver2DRangeSeries( - expr1, (x, -5, 5), (y, -5, 5), - n1=5, n2=5, - params={t: 1}, - use_cm=True, - color_func=lambda x, y, z: np.sqrt(x**2 + y**2), - ) - s3 = ParametricSurfaceSeries( - *expr2, (u, -5, 5), (v, -5, 5), - n1=5, n2=5, - params={t: 1}, - use_cm=True, - color_func=lambda x, y, z, u, v: z - ) - s4 = ParametricSurfaceSeries( - *expr2, (u, -5, 5), (v, -5, 5), - n1=5, n2=5, - params={t: 1}, - use_cm=True, - color_func=lambda x, y, z, u, v: np.sqrt(x**2 + y**2) - ) - return B(s1, s2, s3, s4) - - -def make_test_line_color_func(B, col): - return plot( - cos(x), (x, -3, 3), - backend=B, color_func=col, legend=True, - **options() + return graphics( + surface(expr1, (x, -5, 5), (y, -5, 5), + n1=5, n2=5, + params=params, + use_cm=True, + color_func=lambda x, y, z: z), + surface(expr1, (x, -5, 5), (y, -5, 5), + n1=5, n2=5, + params=params, + use_cm=True, + color_func=lambda x, y, z: np.sqrt(x**2 + y**2)), + surface_parametric( + *expr2, (u, -5, 5), (v, -5, 5), + n1=5, n2=5, + params=params, + use_cm=True, + color_func=lambda x, y, z, u, v: z + ), + surface_parametric( + *expr2, (u, -5, 5), (v, -5, 5), + n1=5, n2=5, + params=params, + use_cm=True, + color_func=lambda x, y, z, u, v: np.sqrt(x**2 + y**2) + ), + backend=B, show=False ) -def make_test_line_interactive_color_func(B): +def make_test_line_color_func(B): expr = t * cos(x * t) - s1 = LineOver1DRangeSeries( - expr, (x, -3, 3), n=5, params={t: 1}, color_func=None - ) - s2 = LineOver1DRangeSeries( - expr, (x, -3, 3), n=5, params={t: 1}, - color_func=lambda x, y: np.cos(x) + params = {t: (1, 0, 2)} + return graphics( + line(expr, (x, -3, 3), n=5, params=params, color_func=None), + line(expr, (x, -3, 3), n=5, params=params, + color_func=lambda x, y: np.cos(x)), + backend=B, show=False ) - return B(s1, s2) def make_test_line_color_plot(B, lc): @@ -948,70 +667,36 @@ def make_test_surface_color_plot3d(B, sc, use_cm, show=False): ) -def make_test_plot3d_list_use_cm_False(B, is_point, is_filled=False): - x = [0, 1, 2, 3, 4, 5] - y = [5, 4, 3, 2, 1, 0] - z = [1, 3, 2, 4, 6, 5] - - return plot3d_list( - x, y, z, +def make_test_plot3d_list(B, is_filled, cf): + z1 = np.linspace(0, 6 * np.pi, 10) + c = np.cos(z1) + s = np.sin(z1) + x1 = z1 * c + y1 = z1 * s + + p1 = plot3d_list(x1, y1, z1, show=False, backend=B, is_point=False, + use_cm=False) + p2 = plot3d_list(x1, y1, z1, show=False, backend=B, is_point=True, + is_filled=is_filled, use_cm=False) + p3 = plot3d_list( + [t * coeff1*coeff2 for coeff1, coeff2 in zip(c, z1)], + [t * coeff1*coeff2 for coeff1, coeff2 in zip(s, z1)], + [t * coeff for coeff in z1], + params={t: (1, 0, 6 * pi)}, backend=B, - is_point=is_point, - is_filled=is_filled, - use_cm=False, show=False, - ) - - -def make_test_plot3d_list_use_cm_color_func( - B, is_point, is_filled=False, cf=None -): - x = [0, 1, 2, 3, 4, 5] - y = [5, 4, 3, 2, 1, 0] - z = [1, 3, 2, 4, 6, 5] - - return plot3d_list( - (x, y, z), - (z, y, x), - backend=B, - is_point=is_point, + is_point=True, is_filled=is_filled, use_cm=True, - show=False, color_func=cf, ) + return p3 + p2 + p1 -def make_test_plot3d_list_interactive(B): - z1 = np.linspace(0, 6 * np.pi, 10) - x1 = z1 * np.cos(z1) - y1 = z1 * np.sin(z1) - - p1 = plot3d_list(x1, y1, z1, show=False, backend=B, is_point=False) - p2 = plot3d_list( - [t * cos(t)], [t * sin(t)], [t], - params={t: (0, 0, 6 * pi)}, - backend=B, - show=False, - is_point=True, - ) - return p2 + p1 - - -def make_test_contour_show_clabels_1(B, clabels): - return plot_contour( - cos(x * y), (x, -2, 2), (y, -2, 2), - backend=B, - is_filled=False, - clabels=clabels, - **options() - ) - - -def make_test_contour_show_clabels_2(B, clabels): +def make_test_contour_show_clabels(B, clabels): return plot_contour( - cos(u * x * y), (x, -2, 2), (y, -2, 2), - params={u: (1, 0, 1)}, + cos(a * x * y), (x, -2, 2), (y, -2, 2), + params={a: (1, 0, 1)}, backend=B, is_filled=False, clabels=clabels, @@ -1045,6 +730,15 @@ def make_test_color_func_expr_2(B): ) +def make_test_plot3d_use_cm(B, use_cm, show=False): + opts = options() + opts["show"] = show + return plot3d( + cos(x**2 + y**2), (x, -1, 1), (y, -1, 1), + backend=B, use_cm=use_cm, **opts + ) + + def make_test_domain_coloring_2d(B, at_infinity): return plot_complex( (z - 1) / (z**2 + z + 1), diff --git a/tests/backends/test_base_backend.py b/tests/backends/test_base_backend.py index 7bbca8c8..a4d668ca 100644 --- a/tests/backends/test_base_backend.py +++ b/tests/backends/test_base_backend.py @@ -74,6 +74,19 @@ def test_common_keywords(): assert p.size == (5, 10) +@pytest.mark.parametrize( + "Backend", [MB, PB, KB, BB] +) +def test_colorloop_colormaps(Backend): + # verify that backends exposes important class attributes enabling + # automatic coloring + + assert hasattr(Backend, "colorloop") + assert isinstance(Backend.colorloop, (list, tuple)) + assert hasattr(Backend, "colormaps") + assert isinstance(Backend.colormaps, (list, tuple)) + + @pytest.mark.skipif(pn is None, reason="panel is not installed") def test_plot_sum(): x, y = symbols("x, y") diff --git a/tests/backends/test_bokeh.py b/tests/backends/test_bokeh.py index db562b3b..d2563a9e 100644 --- a/tests/backends/test_bokeh.py +++ b/tests/backends/test_bokeh.py @@ -16,34 +16,29 @@ from sympy import ( sin, cos, I, pi, Circle, Polygon, sqrt, Matrix, Line, latex, symbols ) -from sympy.abc import x, y, z, u, t +from sympy.abc import a, b, c, x, y, z, u, t from sympy.external import import_module from .make_tests import ( custom_colorloop_1, - make_plot_1, - make_plot_parametric_1, - make_plot3d_parametric_line_1, - make_plot3d_1, - make_plot3d_2, + make_test_plot, + make_test_plot_parametric, + make_test_plot3d_parametric_line, + make_test_plot3d, make_plot3d_wireframe_1, make_plot3d_wireframe_2, make_plot3d_wireframe_3, - make_plot_contour_1, - make_plot_vector_2d_quiver, - make_plot_vector_2d_streamlines_1, - make_plot_vector_2d_streamlines_2, - make_plot_vector_3d_quiver, - make_plot_vector_3d_streamlines_1, - make_plot_vector_2d_normalize_1, - make_plot_vector_2d_normalize_2, - make_plot_vector_2d_quiver_color_func_1, + make_test_plot_contour, + make_test_plot_vector_2d_quiver, + make_test_plot_vector_2d_streamlines, + make_test_plot_vector_3d_quiver_streamlines, + make_test_plot_vector_2d_normalize, + make_test_plot_vector_2d_color_func, make_test_plot_implicit_adaptive_true, make_test_plot_implicit_adaptive_false, make_test_plot_complex_1d, make_test_plot_complex_2d, make_test_plot_complex_3d, - make_test_plot_list_is_filled_false, - make_test_plot_list_is_filled_true, + make_test_plot_list_is_filled, make_test_plot_piecewise_single_series, make_test_plot_piecewise_multiple_series, make_test_plot_geometry_1, @@ -52,20 +47,9 @@ make_test_plot_size, make_test_plot_scale_lin_log, make_test_backend_latex_labels_1, - make_test_plot_use_latex, - make_test_plot_parametric_use_latex, - make_test_plot_contour_use_latex, - make_test_plot_vector_2d_quivers_use_latex, - make_test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex, - make_test_plot_vector_2d_streamlines_custom_scalar_field_use_latex, - make_test_plot_vector_2d_use_latex_colorbar, - make_test_plot_complex_use_latex_1, - make_test_plot_complex_use_latex_2, - make_test_plot_real_imag_use_latex, make_test_plot_polar, make_test_plot_polar_use_cm, make_test_plot3d_implicit, - make_test_line_interactive_color_func, make_test_line_color_plot, make_test_color_func_expr_1, make_test_legend_plot_sum_1, @@ -84,9 +68,9 @@ make_test_root_locus_2, make_test_plot_pole_zero, make_test_poles_zeros_sgrid, - make_test_zgrid, - make_test_sgrid, make_test_ngrid, + make_test_sgrid, + make_test_zgrid, make_test_mcircles, make_test_hvlines ) @@ -108,16 +92,6 @@ class BBchild(BB): colorloop = ["red", "green", "blue"] -def test_colorloop_colormaps(): - # verify that backends exposes important class attributes enabling - # automatic coloring - - assert hasattr(BB, "colorloop") - assert isinstance(BB.colorloop, (list, tuple)) - assert hasattr(BB, "colormaps") - assert isinstance(BB.colormaps, (list, tuple)) - - def test_bokeh_tools(): # verify tools and tooltips on empty Bokeh figure (populated figure # might have different tooltips, tested later on) @@ -152,25 +126,31 @@ def test_custom_colorloop(): assert len(set([r.glyph.line_color for r in f2.renderers])) == 3 -def test_plot(): +@pytest.mark.parametrize( + "use_latex, xlabel, ylabel", [ + (False, "x", "f(x)"), + (True, "$x$", "$f\\left(x\\right)$") + ] +) +def test_plot_1(use_latex, xlabel, ylabel, label_func): # verify that the backends produce the expected results when `plot()` # is called and `rendering_kw` overrides the default line settings - p = make_plot_1(BB, rendering_kw=dict(line_color="red")) - assert len(p.series) == 2 + p = make_test_plot(BB, rendering_kw=dict(line_color="red"), + use_latex=use_latex) + assert len(p.backend.series) == 2 f = p.fig assert len(f.renderers) == 2 assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.Line) - assert f.legend[0].items[0].label["value"] == "sin(x)" + assert f.legend[0].items[0].label["value"] == label_func(use_latex, sin(a * x)) assert f.renderers[0].glyph.line_color == "red" assert isinstance(f.renderers[1].glyph, bokeh.models.glyphs.Line) - assert f.legend[0].items[1].label["value"] == "cos(x)" + assert f.legend[0].items[1].label["value"] == label_func(use_latex, cos(b * x)) assert f.renderers[1].glyph.line_color == "red" assert f.legend[0].visible is True - - p = make_plot_1(BB, rendering_kw=dict(line_color="red"), use_latex=True) - f = p.fig - assert f.legend[0].items[0].label["value"] == "$\\sin{\\left(x \\right)}$" + assert f.xaxis.axis_label == xlabel + assert f.yaxis.axis_label == ylabel + p.backend.update_interactive({a: 2, b: 2}) def test_plot_parametric(): @@ -178,12 +158,23 @@ def test_plot_parametric(): # `plot_parametric()` is called and `rendering_kw` overrides the default # line settings - p = make_plot_parametric_1(BB, rendering_kw=dict(line_color="red")) - assert len(p.series) == 1 + p = make_test_plot_parametric(BB, rendering_kw=dict(line_color="red"), + use_cm=False) + assert len(p.backend.series) == 1 f = p.fig assert len(f.renderers) == 1 - assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.MultiLine) + assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.Line) assert f.renderers[0].glyph.line_color == "red" + p.backend.update_interactive({a: 2, b: 2}) + + # TODO: would love to pass in a colormap, but it's not possible :( + # Hence, test just a line color + p = make_test_plot_parametric(BB, rendering_kw=dict(line_color="red"), + use_cm=True) + assert len(p.backend.series) == 1 + f = p.fig + assert len(f.renderers) == 1 + assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.MultiLine) # 1 colorbar assert len(f.right) == 1 assert f.right[0].title == "x" @@ -196,34 +187,23 @@ def test_plot3d_parametric_line(): # default line settings # Bokeh doesn't support 3D plots - raises( - NotImplementedError, - lambda: make_plot3d_parametric_line_1( - BB, rendering_kw=dict(line_color="red") - ).draw(), + p = make_test_plot3d_parametric_line( + BB, dict(line_color="red"), False, False ) + raises(NotImplementedError, lambda: p.fig) -def test_plot3d(): +def test_plot3d_1(): # verify that the backends produce the expected results when # `plot3d()` is called and `rendering_kw` overrides the default surface # settings # Bokeh doesn't support 3D plots - raises( - NotImplementedError, - lambda: make_plot3d_1( - BB, rendering_kw=dict(colorscale=[[0, "cyan"], [1, "cyan"]]) - ).draw(), + p = make_test_plot3d( + BB, dict(colorscale=[[0, "cyan"], [1, "cyan"]]), + False, False ) - - -def test_plot3d_2(): - # verify that the backends uses string labels when `plot3d()` is called - # with `use_latex=False` and `use_cm=True` - - # Bokeh doesn't support 3D plots - raises(NotImplementedError, lambda: make_plot3d_2(BB).draw()) + raises(NotImplementedError, lambda: p.fig) def test_plot3d_wireframe(): @@ -242,20 +222,28 @@ def test_plot3d_wireframe(): ) -def test_plot_contour(): +@pytest.mark.parametrize( + "use_latex, xl, yl", [ + (False, "x", "y"), + (True, "$x$", "$y$") + ] +) +def test_plot_contour(use_latex, xl, yl, label_func): # verify that the backends produce the expected results when # `plot_contour()` is called and `rendering_kw` overrides the default # surface settings # Bokeh doesn't use rendering_kw dictionary. Nothing to customize yet. - p = make_plot_contour_1(BB, rendering_kw=dict()) - assert len(p.series) == 1 + p = make_test_plot_contour(BB, rendering_kw=dict(), use_latex=use_latex) + assert len(p.backend.series) == 1 f = p.fig assert len(f.renderers) == 1 assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.Image) # 1 colorbar assert len(f.right) == 1 - assert f.right[0].title == str(cos(x**2 + y**2)) + assert f.right[0].title == label_func(use_latex, cos(a*x**2 + y**2)) + assert f.xaxis.axis_label == xl + assert f.yaxis.axis_label == yl @pytest.mark.parametrize( @@ -271,11 +259,11 @@ def test_plot_vector_2d_quivers(pivot, success): # `plot_vector()` is called and `contour_kw`/`quiver_kw` overrides the # default settings - p = make_plot_vector_2d_quiver( + p = make_test_plot_vector_2d_quiver( BB, contour_kw=dict(), quiver_kw=dict(line_color="red", pivot=pivot) ) if success: - assert len(p.series) == 2 + assert len(p.backend.series) == 2 f = p.fig assert len(f.renderers) == 2 assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.Image) @@ -288,62 +276,64 @@ def test_plot_vector_2d_quivers(pivot, success): raises(ValueError, lambda: p.fig) -def test_plot_vector_2d_streamlines_custom_scalar_field(): +@pytest.mark.parametrize( + "scalar, use_latex, expected_label", [ + (True, False, "Magnitude"), + (True, True, "Magnitude"), + (x + y, False, "x + y"), + (x + y, True, "$x + y$"), + ([(x + y), "test"], False, "test"), + ([(x + y), "test"], True, "test") + ] +) +def test_plot_vector_2d_streamlines_custom_scalar_field( + scalar, use_latex, expected_label +): # verify that the backends produce the expected results when # `plot_vector()` is called and `contour_kw`/`stream_kw` overrides the # default settings - p = make_plot_vector_2d_streamlines_1( - BB, stream_kw=dict(line_color="red"), contour_kw=dict() + p = make_test_plot_vector_2d_streamlines( + BB, stream_kw=dict(line_color="red"), contour_kw=dict(), + scalar=scalar, use_latex=use_latex ) - assert len(p.series) == 2 + assert len(p.backend.series) == 2 f = p.fig assert len(f.renderers) == 2 assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.Image) assert isinstance(f.renderers[1].glyph, bokeh.models.glyphs.MultiLine) # 1 colorbar assert len(f.right) == 1 - assert f.right[0].title == "x + y" + assert f.right[0].title == expected_label assert f.renderers[1].glyph.line_color == "red" -def test_plot_vector_2d_streamlines_custom_scalar_field_custom_label(): - # verify that the backends produce the expected results when - # `plot_vector()` is called and `contour_kw`/`stream_kw` overrides the - # default settings - - p = make_plot_vector_2d_streamlines_2( - BB, stream_kw=dict(line_color="red"), contour_kw=dict() - ) - f = p.fig - assert f.right[0].title == "test" - - -def test_plot_vector_3d_quivers(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_vector_3d_quivers(use_latex): # verify that the backends produce the expected results when # `plot_vector()` is called and `quiver_kw` overrides the # default settings # Bokeh doesn't support 3D plots - raises( - NotImplementedError, - lambda: make_plot_vector_3d_quiver( - BB, quiver_kw=dict(sizeref=5)).draw(), - ) + p = make_test_plot_vector_3d_quiver_streamlines( + BB, False, quiver_kw=dict(sizeref=5), use_latex=use_latex) + raises(NotImplementedError, lambda: p.fig) -def test_plot_vector_3d_streamlines(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_vector_3d_streamlines(use_latex): # verify that the backends produce the expected results when # `plot_vector()` is called and `stream_kw` overrides the # default settings # Bokeh doesn't support 3D plots - raises( - NotImplementedError, - lambda: make_plot_vector_3d_streamlines_1( - BB, stream_kw=dict(colorscale=[[0, "red"], [1, "red"]]) - ).draw(), - ) + p = make_test_plot_vector_3d_quiver_streamlines( + BB, True, quiver_kw=dict(sizeref=5), use_latex=use_latex) + raises(NotImplementedError, lambda: p.fig) def test_plot_vector_2d_normalize(): @@ -352,27 +342,9 @@ def test_plot_vector_2d_normalize(): # that data in the figures is different in the two cases normalize=True # and normalize=False - p1 = make_plot_vector_2d_normalize_1(BB, False) - p2 = make_plot_vector_2d_normalize_1(BB, True) - x01 = p1.fig.renderers[0].data_source.data["x0"] - x11 = p1.fig.renderers[0].data_source.data["x1"] - y01 = p1.fig.renderers[0].data_source.data["y0"] - y11 = p1.fig.renderers[0].data_source.data["y1"] - m1 = p1.fig.renderers[0].data_source.data["color_val"] - x02 = p2.fig.renderers[0].data_source.data["x0"] - x12 = p2.fig.renderers[0].data_source.data["x1"] - y02 = p2.fig.renderers[0].data_source.data["y0"] - y12 = p2.fig.renderers[0].data_source.data["y1"] - m2 = p2.fig.renderers[0].data_source.data["color_val"] - assert not np.allclose(x01, x02) - assert not np.allclose(x11, x12) - assert not np.allclose(y01, y02) - assert not np.allclose(y11, y12) - assert np.allclose(m1, m2) - - p1 = make_plot_vector_2d_normalize_2(BB, False) + p1 = make_test_plot_vector_2d_normalize(BB, False) p1.backend.update_interactive({u: 1.5}) - p2 = make_plot_vector_2d_normalize_2(BB, True) + p2 = make_test_plot_vector_2d_normalize(BB, True) p2.backend.update_interactive({u: 1.5}) x01 = p1.fig.renderers[0].data_source.data["x0"] x11 = p1.fig.renderers[0].data_source.data["x1"] @@ -394,11 +366,14 @@ def test_plot_vector_2d_normalize(): def test_plot_vector_2d_quiver_color_func(): # verify that color_func gets applied to 2D quivers - p1 = make_plot_vector_2d_quiver_color_func_1(BB, None) - p2 = make_plot_vector_2d_quiver_color_func_1(BB, lambda x, y, u, v: x) + p1 = make_test_plot_vector_2d_color_func(BB, False, None) + p2 = make_test_plot_vector_2d_color_func(BB, False, lambda x, y, u, v: u) + p3 = make_test_plot_vector_2d_color_func(BB, False, lambda x, y, u, v: u) + p3.backend.update_interactive({a: 1.5}) a1 = p1.fig.renderers[0].data_source.data["color_val"] a2 = p2.fig.renderers[0].data_source.data["color_val"] - assert not np.allclose(a1, a2) + a3 = p3.fig.renderers[0].data_source.data["color_val"] + assert (not np.allclose(a1, a2)) and (not np.allclose(a2, a3)) def test_plot_implicit_adaptive_true(): @@ -426,12 +401,16 @@ def test_plot_implicit_adaptive_false(): ) -def test_plot_real_imag(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_real_imag(use_latex, label_func): # verify that the backends produce the expected results when # `plot_real_imag()` is called and `rendering_kw` overrides the default # settings - p = make_test_real_imag(BB, rendering_kw=dict(line_color="red")) + p = make_test_real_imag(BB, rendering_kw=dict(line_color="red"), + use_latex=use_latex) assert len(p.series) == 2 f = p.fig assert len(f.renderers) == 2 @@ -442,14 +421,20 @@ def test_plot_real_imag(): assert f.legend[0].items[1].label["value"] == "Im(sqrt(x))" assert f.renderers[1].glyph.line_color == "red" assert f.legend[0].visible is True + assert f.xaxis.axis_label == label_func(use_latex, x) + assert f.yaxis.axis_label == r"$f\left(x\right)$" if use_latex else "f(x)" -def test_plot_complex_1d(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_complex_1d(use_latex): # verify that the backends produce the expected results when # `plot_complex()` is called and `rendering_kw` overrides the default # settings - p = make_test_plot_complex_1d(BB, rendering_kw=dict(line_color="red")) + p = make_test_plot_complex_1d(BB, rendering_kw=dict(line_color="red"), + use_latex=use_latex) assert len(p.series) == 1 f = p.fig assert len(f.renderers) == 1 @@ -458,19 +443,27 @@ def test_plot_complex_1d(): # 1 colorbar assert len(f.right) == 1 assert f.right[0].title == "Arg(sqrt(x))" + assert f.xaxis.axis_label == "Real" + assert f.yaxis.axis_label == "Abs" -def test_plot_complex_2d(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_complex_2d(use_latex): # verify that the backends produce the expected results when # `plot_complex()` is called and `rendering_kw` overrides the default # settings - p = make_test_plot_complex_2d(BB, rendering_kw=dict()) + p = make_test_plot_complex_2d(BB, rendering_kw=dict(), + use_latex=use_latex) assert len(p.series) == 1 f = p.fig assert len(f.renderers) == 1 assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.ImageRGBA) assert f.right[0].title == "Argument" + assert f.xaxis.axis_label == "Re" + assert f.yaxis.axis_label == "Im" assert f.toolbar.tools[-1].tooltips == [ ("x", "$x"), ("y", "$y"), @@ -491,26 +484,19 @@ def test_plot_complex_3d(): ) -def test_plot_list_is_filled_false(): +@pytest.mark.parametrize( + "is_filled", [True, False] +) +def test_plot_list_is_filled(is_filled): # verify that the backends produce the expected results when # `plot_list()` is called with `is_filled=False` - p = make_test_plot_list_is_filled_false(BB) + p = make_test_plot_list_is_filled(BB, is_filled) assert len(p.series) == 1 f = p.fig assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.Scatter) - assert f.renderers[0].glyph.line_color != f.renderers[0].glyph.fill_color - - -def test_plot_list_is_filled_true(): - # verify that the backends produce the expected results when - # `plot_list()` is called with `is_filled=True` - - p = make_test_plot_list_is_filled_true(BB) - assert len(p.series) == 1 - f = p.fig - assert isinstance(f.renderers[0].glyph, bokeh.models.glyphs.Scatter) - assert f.renderers[0].glyph.line_color == f.renderers[0].glyph.fill_color + test = f.renderers[0].glyph.line_color == f.renderers[0].glyph.fill_color + assert test is is_filled def test_plot_list_color_func(): @@ -566,48 +552,39 @@ def test_plot_geometry_1(): assert len(f.legend[0].items) == 3 -def test_plot_geometry_2(): +@pytest.mark.parametrize( + "is_filled, n_lines, n_multiline, n_patches, n_scatt, n_legend", [ + (False, 4, 1, 0, 1, 5), + (True, 1, 1, 3, 4, 5), + ] +) +def test_plot_geometry_2( + is_filled, n_lines, n_multiline, n_patches, n_scatt, n_legend +): # verify that is_filled works correctly - p = make_test_plot_geometry_2(BB, False) + p = make_test_plot_geometry_2(BB, is_filled) assert ( len([t.glyph for t in p.fig.renderers if isinstance(t.glyph, bokeh.models.glyphs.Line)]) - == 4 + == n_lines ) assert ( len([t.glyph for t in p.fig.renderers if isinstance(t.glyph, bokeh.models.glyphs.MultiLine)]) - == 1 - ) - assert ( - len([t.glyph for t in p.fig.renderers - if isinstance(t.glyph, bokeh.models.glyphs.Scatter)]) - == 1 - ) - assert len(p.fig.legend[0].items) == 5 - p = make_test_plot_geometry_2(BB, True) - assert ( - len([t.glyph for t in p.fig.renderers - if isinstance(t.glyph, bokeh.models.glyphs.Line)]) - == 1 + == n_multiline ) assert ( len([t.glyph for t in p.fig.renderers if isinstance(t.glyph, bokeh.models.glyphs.Patch)]) - == 3 - ) - assert ( - len([t.glyph for t in p.fig.renderers - if isinstance(t.glyph, bokeh.models.glyphs.MultiLine)]) - == 1 + == n_patches ) assert ( len([t.glyph for t in p.fig.renderers if isinstance(t.glyph, bokeh.models.glyphs.Scatter)]) - == 4 + == n_scatt ) - assert len(p.fig.legend[0].items) == 5 + assert len(p.fig.legend[0].items) == n_legend def test_save(mocker): @@ -700,104 +677,6 @@ def test_backend_latex_labels(): assert p2.ylabel == p2.fig.yaxis.axis_label == "f(x_1^2)" -def test_plot_use_latex(): - # verify that the backends produce the expected results when `plot()` - # is called and `rendering_kw` overrides the default line settings - - p = make_test_plot_use_latex(BB) - f = p.fig - assert f.legend[0].items[0].label["value"] == "$\\sin{\\left(x \\right)}$" - assert f.legend[0].items[1].label["value"] == "$\\cos{\\left(x \\right)}$" - assert f.legend[0].visible is True - - -def test_plot_parametric_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_parametric_use_latex(BB) - f = p.fig - # 1 colorbar - assert len(f.right) == 1 - assert f.right[0].title == "$x$" - - -def test_plot_contour_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_contour_use_latex(BB) - f = p.fig - assert f.right[0].title == "$%s$" % latex(cos(x**2 + y**2)) - - -def test_plot_vector_2d_quivers_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_quivers_use_latex(BB) - f = p.fig - assert f.right[0].title == "Magnitude" - - -def test_plot_vector_2d_streamlines_custom_scalar_field_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_streamlines_custom_scalar_field_use_latex(BB) - f = p.fig - assert f.right[0].title == "$x + y$" - - -def test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex( - BB - ) - f = p.fig - assert f.right[0].title == "test" - - -def test_plot_vector_2d_use_latex_colorbar(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_use_latex_colorbar(BB, True, False) - assert p.fig.right[0].title == "Magnitude" - - p = make_test_plot_vector_2d_use_latex_colorbar(BB, True, True) - assert p.fig.right[0].title == "Magnitude" - - p = make_test_plot_vector_2d_use_latex_colorbar(BB, False, False) - assert p.fig.right[0].title == "$\\left( x, \\ y\\right)$" - - # Bokeh doesn't support gradient streamlines, hence no colorbar - p = make_test_plot_vector_2d_use_latex_colorbar(BB, False, True) - - -@pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_complex_use_latex(): - # complex plot function should return the same result (for axis labels) - # wheter use_latex is True or False - - p = make_test_plot_complex_use_latex_1(BB) - assert p.fig.right[0].title == "Arg(cos(x) + I*sinh(x))" - assert p.fig.xaxis.axis_label == "Real" - assert p.fig.yaxis.axis_label == "Abs" - - p = make_test_plot_complex_use_latex_2(BB) - assert p.fig.right[0].title == "Argument" - assert p.fig.xaxis.axis_label == "Re" - assert p.fig.yaxis.axis_label == "Im" - - -def test_plot_real_imag_use_latex(): - # real/imag plot function should return the same result (for axis labels) - # wheter use_latex is True or False - - p = make_test_plot_real_imag_use_latex(BB) - assert p.fig.xaxis.axis_label == "$x$" - assert p.fig.yaxis.axis_label == r"$f\left(x\right)$" - assert p.fig.legend[0].items[0].label["value"] == "Re(sqrt(x))" - assert p.fig.legend[0].items[1].label["value"] == "Im(sqrt(x))" - - def test_plot_polar(): # verify that 2D polar plot can create plots with cartesian axis and # polar axis @@ -833,21 +712,11 @@ def test_plot3d_implicit(): def test_line_color_func(): - # Verify that backends do not raise errors when plotting lines and that - # the color function is applied. - - p1 = make_test_line_color_func(BB, None) - p2 = make_test_line_color_func(BB, lambda x, y: np.cos(x)) - assert isinstance(p1.fig.renderers[0].glyph, bokeh.models.glyphs.Line) - assert isinstance(p2.fig.renderers[0].glyph, bokeh.models.glyphs.MultiLine) - - -def test_line_interactive_color_func(): # Verify that backends do not raise errors when updating lines and a # color function is applied. - p = make_test_line_interactive_color_func(BB) - p.update_interactive({t: 2}) + p = make_test_line_color_func(BB) + p.backend.update_interactive({t: 2}) assert isinstance(p.fig.renderers[0].glyph, bokeh.models.glyphs.Line) assert isinstance(p.fig.renderers[1].glyph, bokeh.models.glyphs.MultiLine) @@ -870,25 +739,16 @@ def test_update_interactive(): u, v, x, y, z = symbols("u, v, x:z") p = plot( - sin(u * x), - (x, -pi, pi), - adaptive=False, - n=5, - backend=BB, - show=False, + sin(u * x), (x, -pi, pi), + adaptive=False, n=5, backend=BB, show=False, params={u: (1, 0, 2)}, ) p.backend.draw() p.backend.update_interactive({u: 2}) p = plot_parametric( - cos(u * x), - sin(u * x), - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=BB, - show=False, + cos(u * x), sin(u * x), (x, 0, 2 * pi), + adaptive=False, n=5, backend=BB, show=False, params={u: (1, 0, 2)}, use_cm=True, is_point=False, @@ -897,13 +757,8 @@ def test_update_interactive(): p.backend.update_interactive({u: 2}) p = plot_parametric( - cos(u * x), - sin(u * x), - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=BB, - show=False, + cos(u * x), sin(u * x), (x, 0, 2 * pi), + adaptive=False, n=5, backend=BB, show=False, params={u: (1, 0, 2)}, use_cm=True, is_point=True, @@ -912,13 +767,8 @@ def test_update_interactive(): p.backend.update_interactive({u: 2}) p = plot_parametric( - cos(u * x), - sin(u * x), - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=BB, - show=False, + cos(u * x), sin(u * x), (x, 0, 2 * pi), + adaptive=False, n=5, backend=BB, show=False, params={u: (1, 0, 2)}, use_cm=False, ) @@ -926,12 +776,8 @@ def test_update_interactive(): p.backend.update_interactive({u: 2}) p = plot_contour( - cos(u * x**2 + y**2), - (x, -2, 2), - (y, -2, 2), - backend=BB, - show=False, - adaptive=False, + cos(u * x**2 + y**2), (x, -2, 2), (y, -2, 2), + backend=BB, show=False, adaptive=False, n=5, params={u: (1, 0, 2)}, ) @@ -939,12 +785,8 @@ def test_update_interactive(): p.backend.update_interactive({u: 2}) p = plot_vector( - Matrix([-u * y, x]), - (x, -5, 5), - (y, -4, 4), - backend=BB, - n=4, - show=False, + Matrix([-u * y, x]), (x, -5, 5), (y, -4, 4), + backend=BB, n=4, show=False, params={u: (1, 0, 2)}, streamlines=True, ) @@ -952,12 +794,8 @@ def test_update_interactive(): p.backend.update_interactive({u: 2}) p = plot_vector( - Matrix([-u * y, x]), - (x, -5, 5), - (y, -4, 4), - backend=BB, - n=4, - show=False, + Matrix([-u * y, x]), (x, -5, 5), (y, -4, 4), + backend=BB, n=4, show=False, params={u: (1, 0, 2)}, streamlines=False, scalar=True, @@ -966,12 +804,8 @@ def test_update_interactive(): p.backend.update_interactive({u: 2}) p = plot_vector( - Matrix([-u * y, x]), - (x, -5, 5), - (y, -4, 4), - backend=BB, - n=4, - show=False, + Matrix([-u * y, x]), (x, -5, 5), (y, -4, 4), + backend=BB, n=4, show=False, params={u: (1, 0, 2)}, streamlines=False, scalar=False, @@ -1276,7 +1110,7 @@ def test_arrow_2d(): arrows = [t for t in p.fig.center if isinstance(t, Arrow)] assert len(arrows) == 1 assert arrows[0].line_color == "red" - p._backend.update_interactive({a: 4, b: 5}) + p.backend.update_interactive({a: 4, b: 5}) def test_existing_figure_lines(): @@ -1358,7 +1192,7 @@ def test_zgrid( kw["params"] = params p = make_test_zgrid(BB, xi, wn, tp, ts, show_control_axis, **kw) - fig = p._backend.fig if params else p.fig + fig = p.backend.fig if params else p.fig assert len(fig.renderers) == n_lines assert len([t for t in fig.center if isinstance(t, Span)]) == n_hvlines assert len([t for t in fig.center if isinstance(t, LabelSet)]) == n_lblsets @@ -1367,7 +1201,7 @@ def test_zgrid( n_texts ) if params: - p._backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) + p.backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) @pytest.mark.parametrize( @@ -1402,7 +1236,7 @@ def test_sgrid( kw["params"] = params p = make_test_sgrid(BB, xi, wn, tp, ts, auto, show_control_axis, **kw) - fig = p._backend.fig if params else p.fig + fig = p.backend.fig if params else p.fig assert len(fig.renderers) == n_lines assert len([t for t in fig.center if isinstance(t, Span)]) == n_hvlines assert len([t for t in fig.center if isinstance(t, LabelSet)]) == n_lblsets @@ -1411,7 +1245,7 @@ def test_sgrid( n_texts ) if params: - p._backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) + p.backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) @pytest.mark.parametrize( @@ -1444,7 +1278,7 @@ def test_plot_pole_zero(sgrid, zgrid, T, is_filled): p = make_test_plot_pole_zero(BB, sgrid=sgrid, zgrid=zgrid, T=T, is_filled=is_filled) fig = p.fig - p._backend.update_interactive({a: 2}) + p.backend.update_interactive({a: 2}) @pytest.mark.filterwarnings("ignore::UserWarning") @@ -1453,7 +1287,7 @@ def test_plot_poles_zeros_sgrid(): a = symbols("a") p = make_test_poles_zeros_sgrid(BB) - fig = p._backend.fig + fig = p.backend.fig xlim = fig.x_range.start, fig.x_range.end ylim = fig.y_range.start, fig.y_range.end assert (xlim is not None) and (ylim is not None) @@ -1461,7 +1295,7 @@ def test_plot_poles_zeros_sgrid(): # the code for better positioning the grid... assert xlim[0] > -5 and xlim[1] < 2 assert ylim[0] > -5 and ylim[1] < 5 - p._backend.update_interactive({a: 2}) + p.backend.update_interactive({a: 2}) @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -1476,13 +1310,13 @@ def test_plot_root_locus_1( ): a = symbols("a") p = make_test_root_locus_1(BB, sgrid, zgrid) - assert isinstance(p._backend, BB) - assert len(p._backend.series) == 2 + assert isinstance(p.backend, BB) + assert len(p.backend.series) == 2 # NOTE: the backend is going to reorder data series such that grid # series are placed at the end. - assert isinstance(p._backend[0], RootLocusSeries) - assert isinstance(p._backend[1], instance) - fig = p._backend.fig + assert isinstance(p.backend[0], RootLocusSeries) + assert isinstance(p.backend[1], instance) + fig = p.backend.fig assert len(fig.renderers) == n_renderers assert len([t for t in fig.renderers if isinstance(t.glyph, BLine)]) == n_lines assert len([t for t in fig.renderers if isinstance(t.glyph, Scatter)]) == 2 @@ -1490,7 +1324,7 @@ def test_plot_root_locus_1( assert len([t for t in fig.center if isinstance(t, Span)]) == 2 line_colors = {'#1f77b4', '#aaa'} assert all(t.glyph.line_color in line_colors for t in fig.renderers) - p._backend.update_interactive({a: 2}) + p.backend.update_interactive({a: 2}) @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -1661,10 +1495,10 @@ def test_plot_nichols_lines_scatter(scatter, use_cm, instance): # with nichols grid lines p = plot_nichols(tf, ngrid=False, show=False, n=10, backend=BB, scatter=scatter, use_cm=use_cm, params={a: (5, 0, 10)}) - fig = p._backend.fig + fig = p.backend.fig assert len(fig.renderers) == 1 assert isinstance(fig.renderers[0].glyph, instance) - p._backend.update_interactive({a: 6}) + p.backend.update_interactive({a: 6}) def test_hvlines(): @@ -1675,4 +1509,4 @@ def test_hvlines(): assert len(lines) == 2 assert lines[0].dimension == "width" assert lines[1].dimension == "height" - p._backend.update_interactive({a: 3, b: 4}) + p.backend.update_interactive({a: 3, b: 4}) diff --git a/tests/backends/test_k3d.py b/tests/backends/test_k3d.py index f7844208..2cee555c 100644 --- a/tests/backends/test_k3d.py +++ b/tests/backends/test_k3d.py @@ -17,57 +17,45 @@ from sympy.abc import x, y, z, u, t, a, b, c from .make_tests import ( custom_colorloop_2, - make_plot_1, - make_plot_parametric_1, - make_plot3d_parametric_line_1, - make_plot3d_1, - make_plot3d_2, + make_test_plot, + make_test_plot_parametric, + make_test_plot3d_parametric_line, + make_test_plot3d, make_plot3d_wireframe_1, make_plot3d_wireframe_2, make_plot3d_wireframe_3, - make_plot_contour_1, - make_plot_vector_2d_quiver, - make_plot_vector_2d_streamlines_1, - make_plot_vector_3d_quiver, - make_plot_vector_3d_streamlines_1, - make_plot_vector_3d_normalize_1, - make_plot_vector_3d_normalize_2, - make_plot_vector_3d_quiver_color_func_1, - make_plot_vector_3d_quiver_color_func_2, - make_plot_vector_3d_streamlines_color_func, + make_test_plot_contour, + make_test_plot_vector_2d_quiver, + make_test_plot_vector_2d_streamlines, + make_test_plot_vector_3d_quiver_streamlines, + make_test_plot_vector_3d_normalize, + make_test_plot_vector_3d_quiver_color_func, + make_test_plot_vector_3d_streamlines_color_func, make_test_plot_implicit_adaptive_true, make_test_plot_implicit_adaptive_false, make_test_plot_complex_1d, make_test_plot_complex_2d, make_test_plot_complex_3d, - make_test_plot_list_is_filled_false, - make_test_plot_list_is_filled_true, + make_test_plot_list_is_filled, make_test_plot_piecewise_single_series, make_test_plot_piecewise_multiple_series, make_test_plot_geometry_1, make_test_plot_geometry_3d, make_test_backend_latex_labels_2, - make_test_plot_vector_3d_quivers_use_latex, - make_test_plot_vector_3d_streamlines_use_latex, - make_test_plot3d_use_cm, + make_test_plot_polar, make_test_plot3d_implicit, - make_test_surface_color_func_1, - make_test_surface_color_func_2, - make_test_surface_interactive_color_func, + make_test_surface_color_func, + make_test_line_color_plot, make_test_line_color_plot3d_parametric_line, make_test_surface_color_plot3d, - make_test_plot3d_list_use_cm_False, - make_test_plot3d_list_use_cm_color_func, - make_test_plot3d_list_interactive, + make_test_plot3d_list, make_test_color_func_expr_2, make_test_analytic_landscape, + make_test_detect_poles, make_test_parametric_texts_3d, - make_test_plot3d_parametric_line_use_latex, - make_test_plot3d_use_latex, - make_test_vectors_3d_update_interactive, make_test_plot_list_color_func, make_test_real_imag, - make_test_arrow_3d + make_test_arrow_3d, ) @@ -88,16 +76,6 @@ class KBchild1(KB): colorloop = [(1, 0, 0), (0, 1, 0), (0, 0, 1)] -def test_colorloop_colormaps(): - # verify that backends exposes important class attributes enabling - # automatic coloring - - assert hasattr(KB, "colorloop") - assert isinstance(KB.colorloop, (list, tuple)) - assert hasattr(KB, "colormaps") - assert isinstance(KB.colormaps, (list, tuple)) - - def test_custom_colorloop(): # verify that it is possible to modify the backend's class attributes # in order to change custom coloring @@ -115,15 +93,13 @@ def test_custom_colorloop(): assert len(set([o.color for o in f2.objects])) == 3 -def test_plot(): +def test_plot_1(): # verify that the backends produce the expected results when `plot()` # is called and `rendering_kw` overrides the default line settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_plot_1(KB, rendering_kw=dict(line_color="red")).draw(), - ) + p = make_test_plot(KB, rendering_kw=dict(line_color="red")) + raises(NotImplementedError, lambda: p.fig) def test_plot_parametric(): @@ -131,12 +107,10 @@ def test_plot_parametric(): # `plot_parametric()` is called and `rendering_kw` overrides the default # line settings - raises( - NotImplementedError, - lambda: make_plot_parametric_1( - KB, rendering_kw=dict(line_color="red") - ).draw(), - ) + # K3D doesn't support 2D plots + p = make_test_plot_parametric( + KB, rendering_kw=dict(line_color="red"), use_cm=False) + raises(NotImplementedError, lambda: p.fig) def test_plot3d_parametric_line(): @@ -144,38 +118,57 @@ def test_plot3d_parametric_line(): # `plot3d_parametric_line()` is called and `rendering_kw` overrides the # default line settings - p = make_plot3d_parametric_line_1(KB, rendering_kw=dict(color=16711680)) - assert len(p.series) == 1 + p = make_test_plot3d_parametric_line( + KB, rendering_kw=dict(color=16711680), use_latex=False, use_cm=False) + assert len(p.backend.series) == 1 f = p.fig assert len(f.objects) == 1 assert isinstance(f.objects[0], k3d.objects.Line) assert f.objects[0].color == 16711680 assert f.objects[0].name is None - - -def test_plot3d(): + p.backend.update_interactive({a: 2, b: 2}) + + p1 = make_test_plot3d_parametric_line( + KB, rendering_kw=dict(), use_latex=False, use_cm=True) + p1.backend.update_interactive({a: 2, b: 2}) + p2 = make_test_plot3d_parametric_line( + KB, rendering_kw=dict(color_map=k3d.basic_color_maps.Blues), + use_latex=False, use_cm=True) + p2.backend.update_interactive({a: 2, b: 2}) + assert len(p1.fig.objects[0].color_map) != len(p2.fig.objects[0].color_map) + + +@pytest.mark.parametrize( + "use_latex, xl, yl, zl", [ + (True, "x", "y", "f\\left(x, y\\right)"), + (False, "x", "y", "f(x, y)"), + ] +) +def test_plot3d_1(use_latex, xl, yl, zl): # verify that the backends produce the expected results when # `plot3d()` is called and `rendering_kw` overrides the default surface # settings - p = make_plot3d_1(KB, rendering_kw=dict(color=16711680)) - assert len(p.series) == 1 + p = make_test_plot3d(KB, rendering_kw=dict(color=16711680), + use_cm=False, use_latex=use_latex) + assert len(p.backend.series) == 2 f = p.fig - assert len(f.objects) == 1 + assert len(f.objects) == 2 assert isinstance(f.objects[0], k3d.objects.Mesh) assert f.objects[0].color == 16711680 assert f.objects[0].name is None + assert p.fig.axes == [xl, yl, zl] + p.backend.update_interactive({a: 2, b: 2}) - -def test_plot3d_2(): - # verify that the backends uses string labels when `plot3d()` is called - # with `use_latex=False` and `use_cm=True` - - p = make_plot3d_2(KB) - assert len(p.series) == 2 + p = make_test_plot3d(KB, + rendering_kw=dict(color_map=k3d.basic_color_maps.Blues), + use_cm=True, use_latex=use_latex) + assert len(p.backend.series) == 2 f = p.fig assert len(f.objects) == 2 - assert p.fig.axes == ["x", "y", "f(x, y)"] + assert np.allclose(f.objects[0].color_map, f.objects[1].color_map) + assert p.fig.axes == [xl, yl, zl] + p.backend.update_interactive({a: 2, b: 2}) def test_plot3d_wireframe(): @@ -207,10 +200,8 @@ def test_plot_contour(): # surface settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_plot_contour_1(KB, rendering_kw=dict()).draw() - ) + p = make_test_plot_contour(KB, rendering_kw=dict(), use_latex=False) + raises(NotImplementedError, lambda: p.fig) def test_plot_vector_2d_quivers(): @@ -219,12 +210,9 @@ def test_plot_vector_2d_quivers(): # default settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_plot_vector_2d_quiver( - KB, quiver_kw=dict(), contour_kw=dict() - ).draw(), - ) + p = make_test_plot_vector_2d_quiver( + KB, quiver_kw=dict(), contour_kw=dict()) + raises(NotImplementedError, lambda: p.fig) def test_plot_vector_2d_streamlines_custom_scalar_field(): @@ -233,12 +221,10 @@ def test_plot_vector_2d_streamlines_custom_scalar_field(): # default settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_plot_vector_2d_streamlines_1( - KB, stream_kw=dict(), contour_kw=dict() - ).draw(), - ) + p = make_test_plot_vector_2d_streamlines( + KB, stream_kw=dict(), contour_kw=dict(), scalar=True, + use_latex=False) + raises(NotImplementedError, lambda: p.fig) def test_plot_vector_3d_quivers(): @@ -246,14 +232,29 @@ def test_plot_vector_3d_quivers(): # `plot_vector()` is called and `quiver_kw` overrides the # default settings - p = make_plot_vector_3d_quiver( - KB, quiver_kw=dict(scale=0.5, color=16711680), use_cm=False + p = make_test_plot_vector_3d_quiver_streamlines( + KB, False, quiver_kw=dict(scale=0.5, color=16711680), use_cm=False, + use_latex=False ) - assert len(p.series) == 1 + assert len(p.backend.series) == 1 f = p.fig assert len(f.objects) == 1 assert isinstance(f.objects[0], k3d.objects.Vectors) - assert all([c == 16711680 for c in f.objects[0].colors]) + c1 = f.objects[0].colors + assert all([c == 16711680 for c in c1]) + p.backend.update_interactive({a: 2}) + + p = make_test_plot_vector_3d_quiver_streamlines( + KB, False, quiver_kw=dict(color_map=k3d.basic_color_maps.Blues), + use_cm=True, use_latex=False + ) + assert len(p.backend.series) == 1 + f = p.fig + assert len(f.objects) == 1 + assert isinstance(f.objects[0], k3d.objects.Vectors) + c2 = f.objects[0].colors + assert not np.allclose(c1, c2) + p.backend.update_interactive({a: 2}) def test_plot_vector_3d_streamlines(): @@ -261,17 +262,21 @@ def test_plot_vector_3d_streamlines(): # `plot_vector()` is called and `stream_kw` overrides the # default settings - p = make_plot_vector_3d_streamlines_1(KB, stream_kw=dict(color=16711680)) - assert len(p.series) == 1 + p = make_test_plot_vector_3d_quiver_streamlines( + KB, True, stream_kw=dict(color=16711680), use_latex=False) + assert len(p.backend.series) == 1 f = p.fig assert len(f.objects) == 1 assert isinstance(f.objects[0], k3d.objects.Line) assert f.objects[0].color == 16711680 + raises(NotImplementedError, lambda: p.backend.update_interactive({a: 2})) # test different combinations for streamlines: it should not raise errors - p = make_plot_vector_3d_streamlines_1(KB, stream_kw=dict(starts=True)) - p = make_plot_vector_3d_streamlines_1( - KB, + p = make_test_plot_vector_3d_quiver_streamlines( + KB, True, stream_kw=dict(starts=True)) + raises(NotImplementedError, lambda: p.backend.update_interactive({a: 2})) + p = make_test_plot_vector_3d_quiver_streamlines( + KB, True, stream_kw=dict( starts={ "x": np.linspace(-5, 5, 10), @@ -280,11 +285,13 @@ def test_plot_vector_3d_streamlines(): } ), ) + raises(NotImplementedError, lambda: p.backend.update_interactive({a: 2})) # other keywords: it should not raise errors - p = make_plot_vector_3d_streamlines_1( - KB, stream_kw=dict(), kwargs=dict(use_cm=False) + p = make_test_plot_vector_3d_quiver_streamlines( + KB, True, stream_kw=dict(), kwargs=dict(use_cm=False) ) + raises(NotImplementedError, lambda: p.backend.update_interactive({a: 2})) @pytest.mark.filterwarnings("ignore::RuntimeWarning") @@ -294,15 +301,15 @@ def test_plot_vector_3d_normalize(): # that data in the figures is different in the two cases normalize=True # and normalize=False - p1 = make_plot_vector_3d_normalize_1(KB, False) - p2 = make_plot_vector_3d_normalize_1(KB, True) + p1 = make_test_plot_vector_3d_normalize(KB, False) + p2 = make_test_plot_vector_3d_normalize(KB, True) assert not np.allclose( p1.fig.objects[0].vectors, p2.fig.objects[0].vectors ) - p1 = make_plot_vector_3d_normalize_2(KB, False) + p1 = make_test_plot_vector_3d_normalize(KB, False) p1.backend.update_interactive({u: 1.5}) - p2 = make_plot_vector_3d_normalize_2(KB, True) + p2 = make_test_plot_vector_3d_normalize(KB, True) p2.backend.update_interactive({u: 1.5}) assert not np.allclose( p1.fig.objects[0].vectors, p2.fig.objects[0].vectors @@ -312,15 +319,17 @@ def test_plot_vector_3d_normalize(): def test_plot_vector_3d_quivers_color_func(): # verify that color_func gets applied to 3D quivers - p1 = make_plot_vector_3d_quiver_color_func_1(KB, None) - p2 = make_plot_vector_3d_quiver_color_func_1( + p1 = make_test_plot_vector_3d_quiver_color_func(KB, None) + p2 = make_test_plot_vector_3d_quiver_color_func( KB, lambda x, y, z, u, v, w: x) assert not np.allclose(p1.fig.objects[0].colors, p2.fig.objects[0].colors) + p1.backend.update_interactive({a: 2}) + p2.backend.update_interactive({a: 2}) - p1 = make_plot_vector_3d_quiver_color_func_2(KB, None) - p2 = make_plot_vector_3d_quiver_color_func_2( + p1 = make_test_plot_vector_3d_quiver_color_func(KB, None) + p2 = make_test_plot_vector_3d_quiver_color_func( KB, lambda x, y, z, u, v, w: np.cos(u)) - p3 = make_plot_vector_3d_quiver_color_func_2( + p3 = make_test_plot_vector_3d_quiver_color_func( KB, lambda x, y, z, u, v, w: np.cos(u)) assert not np.allclose(p1.fig.objects[0].colors, p2.fig.objects[0].colors) p3.backend.update_interactive({a: 2}) @@ -330,11 +339,13 @@ def test_plot_vector_3d_quivers_color_func(): def test_plot_vector_3d_streamlines_color_func(): # verify that color_func gets applied to 3D quivers - p1 = make_plot_vector_3d_streamlines_color_func(KB, None) - p2 = make_plot_vector_3d_streamlines_color_func(KB, lambda x, y, z, u, v, w: x) + p1 = make_test_plot_vector_3d_streamlines_color_func(KB, None) + p2 = make_test_plot_vector_3d_streamlines_color_func(KB, lambda x, y, z, u, v, w: x) assert not np.allclose( p1.fig.objects[0].attribute, p2.fig.objects[0].attribute ) + raises(NotImplementedError, lambda: p1.backend.update_interactive({a: 2})) + raises(NotImplementedError, lambda: p2.backend.update_interactive({a: 2})) def test_plot_implicit_adaptive_true(): @@ -342,12 +353,10 @@ def test_plot_implicit_adaptive_true(): # `plot_implicit()` is called with `adaptive=True` # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_plot_implicit_adaptive_true( - KB, rendering_kw=dict() - ).draw() + p = make_test_plot_implicit_adaptive_true( + KB, rendering_kw=dict() ) + raises(NotImplementedError, lambda: p.fig) def test_plot_implicit_adaptive_false(): @@ -356,10 +365,8 @@ def test_plot_implicit_adaptive_false(): # overrides the default settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_plot_implicit_adaptive_false(KB, rendering_kw=dict()).draw(), - ) + p = make_test_plot_implicit_adaptive_false(KB, rendering_kw=dict()) + raises(NotImplementedError, lambda: p.fig) def test_plot_real_imag(): @@ -368,10 +375,8 @@ def test_plot_real_imag(): # settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_real_imag(KB, rendering_kw=dict()).draw() - ) + p = make_test_real_imag(KB, rendering_kw=dict(), use_latex=False) + raises(NotImplementedError, lambda: p.fig) def test_plot_complex_1d(): @@ -380,10 +385,8 @@ def test_plot_complex_1d(): # settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_plot_complex_1d(KB, rendering_kw=dict()).draw(), - ) + p = make_test_plot_complex_1d(KB, rendering_kw=dict(), use_latex=False) + raises(NotImplementedError, lambda: p.fig) def test_plot_complex_2d(): @@ -392,10 +395,8 @@ def test_plot_complex_2d(): # settings # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_plot_complex_2d(KB, rendering_kw=dict()).draw(), - ) + p = make_test_plot_complex_2d(KB, rendering_kw=dict()) + raises(NotImplementedError, lambda: p.fig) def test_plot_complex_3d(): @@ -411,26 +412,16 @@ def test_plot_complex_3d(): assert f.objects[0].name is None -def test_plot_list_is_filled_false(): +@pytest.mark.parametrize( + "is_filled", [True, False] +) +def test_plot_list(is_filled): # verify that the backends produce the expected results when # `plot_list()` is called with `is_filled=False` # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_plot_list_is_filled_false(KB).draw() - ) - - -def test_plot_list_is_filled_true(): - # verify that the backends produce the expected results when - # `plot_list()` is called with `is_filled=True` - - # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_plot_list_is_filled_true(KB).draw() - ) + p = make_test_plot_list_is_filled(KB, is_filled=is_filled) + raises(NotImplementedError, lambda: p.fig) def test_plot_list_color_func(): @@ -438,10 +429,8 @@ def test_plot_list_color_func(): # `plot_list()` is called with `color_func` # K3D doesn't support 2D plots - raises( - NotImplementedError, - lambda: make_test_plot_list_color_func(KB).draw() - ) + p = make_test_plot_list_color_func(KB) + raises(NotImplementedError, lambda: p.fig) def test_plot_piecewise_single_series(): @@ -471,7 +460,8 @@ def test_plot_geometry_1(): # `plot_geometry()` is called # K3D doesn't support 2D plots - raises(NotImplementedError, lambda: make_test_plot_geometry_1(KB).draw()) + p = make_test_plot_geometry_1(KB) + raises(NotImplementedError, lambda: p.fig) def test_plot_geometry_3d(): @@ -526,17 +516,6 @@ def test_save(): ) -def test_vectors_3d_update_interactive(): - # Some backends do not support streamlines with iplot. Test that the - # backends raise error. - - p = make_test_vectors_3d_update_interactive(KB) - raises( - NotImplementedError, - lambda: p.backend.update_interactive({a: 2, b: 2, c: 2}) - ) - - def test_backend_latex_labels(): # verify that backends are going to set axis latex-labels in the # 2D and 3D case @@ -551,50 +530,6 @@ def test_backend_latex_labels(): assert p2.zlabel == p2.fig.axes[2] == "f(x_1^2, x_2)" -def test_plot3d_parametric_line_use_latex(): - # verify that the colorbar uses latex label - - # NOTE: K3D doesn't show a label to colorbar - make_test_plot3d_parametric_line_use_latex(KB) - - -def test_plot3d_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot3d_use_latex(KB) - p.fig - assert p.fig.axes == ["x", "y", "f\\left(x, y\\right)"] - - -def test_plot_vector_3d_quivers_use_latex(): - # verify that the colorbar uses latex label - - # K3D doesn't show label on colorbar - p = make_test_plot_vector_3d_quivers_use_latex(KB) - assert len(p.series) == 1 - - -def test_plot_vector_3d_streamlines_use_latex(): - # verify that the colorbar uses latex label - - # K3D doesn't show labels on colorbar - p = make_test_plot_vector_3d_streamlines_use_latex(KB) - assert len(p.series) == 1 - - -def test_plot3d_use_cm(): - # verify that use_cm produces the expected results on plot3d - - p1 = make_test_plot3d_use_cm(KB, True) - p2 = make_test_plot3d_use_cm(KB, False) - n1 = len(p1.fig.objects[0].color_map) - n2 = len(p2.fig.objects[0].color_map) - if n1 == n2: - assert not np.allclose( - p1.fig.objects[0].color_map, p2.fig.objects[0].color_map - ) - - def test_k3d_vector_pivot(): # verify that K3DBackend accepts quiver_kw={"pivot": "something"} and # produces different results @@ -640,37 +575,14 @@ def test_surface_color_func(): # Verify that backends do not raise errors when plotting surfaces and that # the color function is applied. - p1 = make_test_surface_color_func_1(KB, lambda x, y, z: z) - p2 = make_test_surface_color_func_1( - KB, lambda x, y, z: np.sqrt(x**2 + y**2) - ) - assert not np.allclose( - p1.fig.objects[0].attribute, p2.fig.objects[0].attribute - ) - - p1 = make_test_surface_color_func_2(KB, lambda x, y, z, u, v: z) - p2 = make_test_surface_color_func_2( - KB, lambda x, y, z, u, v: np.sqrt(x**2 + y**2) - ) - assert not np.allclose( - p1.fig.objects[0].attribute, p2.fig.objects[0].attribute - ) - - -def test_surface_interactive_color_func(): - # After the addition of `color_func`, `SurfaceInteractiveSeries` and - # `ParametricSurfaceInteractiveSeries` returns different elements. - # Verify that backends do not raise errors when updating surfaces and a - # color function is applied. - - p = make_test_surface_interactive_color_func(KB) - p.update_interactive({t: 2}) + p = make_test_surface_color_func(KB) assert not np.allclose( p.fig.objects[0].attribute, p.fig.objects[1].attribute ) assert not np.allclose( p.fig.objects[2].attribute, p.fig.objects[3].attribute ) + p.backend.update_interactive({t: 2}) def test_line_color_plot3d_parametric_line(): @@ -814,53 +726,39 @@ def test_update_interactive(): p.backend.update_interactive({u: 2}) -def test_plot3d_list_use_cm_False(): +def test_plot3d_list(): # verify that plot3d_list produces the expected results when no color map # is required - # solid color line - p = make_test_plot3d_list_use_cm_False(KB, False, False) - assert isinstance(p.fig.objects[0], k3d.objects.Line) - - # solid color markers with empty faces # NOTE: k3d doesn't support is_filled - p = make_test_plot3d_list_use_cm_False(KB, True, False) - assert isinstance(p.fig.objects[0], k3d.objects.Points) - # solid color markers with filled faces - # NOTE: k3d doesn't support is_filled - p = make_test_plot3d_list_use_cm_False(KB, True, True) + p = make_test_plot3d_list(KB, False, None) + assert len(p.fig.objects) == 3 assert isinstance(p.fig.objects[0], k3d.objects.Points) - - -def test_plot3d_list_use_cm_color_func(): - # verify that use_cm=True and color_func do their job - - # NOTE: k3d doesn't support is_filled - - # line with colormap - # if color_func is not provided, the same parameter will be used - # for all points - p1 = make_test_plot3d_list_use_cm_color_func(KB, False, False, None) - c1 = p1.fig.objects[0].attribute - p2 = make_test_plot3d_list_use_cm_color_func(KB, False, False, lambda x, y, z: x) - c2 = p2.fig.objects[0].attribute - assert not np.allclose(c1, c2) - - # markers with empty faces - p1 = make_test_plot3d_list_use_cm_color_func(KB, False, False, None) - c1 = p1.fig.objects[0].attribute - p2 = make_test_plot3d_list_use_cm_color_func( - KB, False, False, lambda xx, yy, zz: xx) - c2 = p2.fig.objects[0].attribute - assert not np.allclose(c1, c2) - - -def test_plot3d_list_interactive(): - # verify that no errors are raises while updating a plot3d_list - - p = make_test_plot3d_list_interactive(KB) - p.backend.update_interactive({t: 1}) + assert isinstance(p.fig.objects[1], k3d.objects.Points) + assert isinstance(p.fig.objects[2], k3d.objects.Line) + a1 = p.fig.objects[0].attribute + assert np.allclose(a1, 0) + assert len(p.fig.objects[1].attribute) == 0 + assert len(p.fig.objects[2].attribute) == 0 + assert p.fig.objects[0].color == 2062260 + assert p.fig.objects[1].color == 16744206 + assert p.fig.objects[2].color == 2924588 + p.backend.update_interactive({t: 2}) + + p = make_test_plot3d_list(KB, False, lambda x, y, z: x) + assert len(p.fig.objects) == 3 + assert isinstance(p.fig.objects[0], k3d.objects.Points) + assert isinstance(p.fig.objects[1], k3d.objects.Points) + assert isinstance(p.fig.objects[2], k3d.objects.Line) + a2 = p.fig.objects[0].attribute + assert not np.allclose(a2, 0) + assert len(p.fig.objects[1].attribute) == 0 + assert len(p.fig.objects[2].attribute) == 0 + assert p.fig.objects[0].color == 2062260 + assert p.fig.objects[1].color == 16744206 + assert p.fig.objects[2].color == 2924588 + p.backend.update_interactive({t: 2}) def test_color_func_expr(): @@ -957,4 +855,4 @@ def test_arrow_3d(): assert len(fig.objects) == 1 assert fig.objects[0].origin_color == 0xff0000 assert fig.objects[0].head_color == 0xff0000 - p._backend.update_interactive({a: 4, b: 5, c: 6}) + p.backend.update_interactive({a: 4, b: 5, c: 6}) diff --git a/tests/backends/test_matplotlib.py b/tests/backends/test_matplotlib.py index 1022e682..71a9ceff 100644 --- a/tests/backends/test_matplotlib.py +++ b/tests/backends/test_matplotlib.py @@ -25,36 +25,29 @@ from sympy.external import import_module from .make_tests import ( custom_colorloop_1, - make_plot_1, - make_plot_parametric_1, - make_plot3d_parametric_line_1, - make_plot3d_1, - make_plot3d_2, + make_test_plot, + make_test_plot_parametric, + make_test_plot3d_parametric_line, + make_test_plot3d, make_plot3d_wireframe_1, make_plot3d_wireframe_2, make_plot3d_wireframe_3, - make_plot_contour_1, + make_test_plot_contour, make_plot_contour_is_filled, - make_plot_vector_2d_quiver, - make_plot_vector_2d_streamlines_1, - make_plot_vector_2d_streamlines_2, - make_plot_vector_3d_quiver, - make_plot_vector_3d_streamlines_1, - make_plot_vector_2d_normalize_1, - make_plot_vector_2d_normalize_2, - make_plot_vector_3d_normalize_1, - make_plot_vector_3d_normalize_2, - make_plot_vector_2d_quiver_color_func_1, - make_plot_vector_3d_quiver_color_func_1, - make_plot_vector_3d_quiver_color_func_2, - make_plot_vector_3d_streamlines_color_func, + make_test_plot_vector_2d_quiver, + make_test_plot_vector_2d_streamlines, + make_test_plot_vector_3d_quiver_streamlines, + make_test_plot_vector_2d_normalize, + make_test_plot_vector_3d_normalize, + make_test_plot_vector_2d_color_func, + make_test_plot_vector_3d_quiver_color_func, + make_test_plot_vector_3d_streamlines_color_func, make_test_plot_implicit_adaptive_true, make_test_plot_implicit_adaptive_false, make_test_plot_complex_1d, make_test_plot_complex_2d, make_test_plot_complex_3d, - make_test_plot_list_is_filled_false, - make_test_plot_list_is_filled_true, + make_test_plot_list_is_filled, make_test_plot_piecewise_single_series, make_test_plot_piecewise_multiple_series, make_test_plot_geometry_1, @@ -66,34 +59,15 @@ make_test_plot_scale_lin_log, make_test_backend_latex_labels_1, make_test_backend_latex_labels_2, - make_test_plot_use_latex, - make_test_plot_parametric_use_latex, - make_test_plot_contour_use_latex, - make_test_plot_vector_2d_quivers_use_latex, - make_test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex, - make_test_plot_vector_2d_streamlines_custom_scalar_field_use_latex, - make_test_plot_vector_2d_use_latex_colorbar, - make_test_plot_vector_3d_quivers_use_latex, - make_test_plot_vector_3d_streamlines_use_latex, - make_test_plot_complex_use_latex_1, - make_test_plot_complex_use_latex_2, - make_test_plot_real_imag_use_latex, - make_test_plot3d_use_cm, make_test_plot_polar, make_test_plot_polar_use_cm, make_test_plot3d_implicit, - make_test_surface_color_func_1, - make_test_surface_color_func_2, - make_test_surface_interactive_color_func, - make_test_line_interactive_color_func, + make_test_surface_color_func, make_test_line_color_plot, make_test_line_color_plot3d_parametric_line, make_test_surface_color_plot3d, - make_test_plot3d_list_use_cm_False, - make_test_plot3d_list_use_cm_color_func, - make_test_plot3d_list_interactive, - make_test_contour_show_clabels_1, - make_test_contour_show_clabels_2, + make_test_plot3d_list, + make_test_contour_show_clabels, make_test_color_func_expr_1, make_test_color_func_expr_2, make_test_legend_plot_sum_1, @@ -108,9 +82,6 @@ make_test_parametric_texts_2d, make_test_parametric_texts_3d, make_test_line_color_func, - make_test_plot3d_parametric_line_use_latex, - make_test_plot3d_use_latex, - make_test_vectors_3d_update_interactive, make_test_plot_list_color_func, make_test_real_imag, make_test_arrow_2d, @@ -146,21 +117,10 @@ class MBchild(MB): colorloop = ["red", "green", "blue"] -def test_colorloop_colormaps(): - # verify that backends exposes important class attributes enabling - # automatic coloring - - assert hasattr(MB, "colorloop") - assert isinstance(MB.colorloop, (list, tuple)) - assert hasattr(MB, "colormaps") - assert isinstance(MB.colormaps, (list, tuple)) - - def test_MatplotlibBackend(): # verify that MB keeps track of the handles and a few other important # keyword arguments - # `_handle` is needed in order to correctly update the data with iplot x, y = symbols("x, y") p = plot3d( cos(x**2 + y**2), @@ -189,105 +149,158 @@ def test_custom_colorloop(): assert len(set([l.get_color() for l in f2.axes[0].lines])) == 3 -def test_plot(): +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +@pytest.mark.parametrize( + "use_latex, xlabel, ylabel", [ + (False, "x", "f(x)"), + (True, "$x$", "$f\\left(x\\right)$") + ] +) +def test_plot_1(use_latex, xlabel, ylabel, label_func): # verify that the backends produce the expected results when `plot()` # is called and `rendering_kw` overrides the default line settings - p = make_plot_1(MB, rendering_kw=dict(color="red")) - assert len(p.series) == 2 + p = make_test_plot(MB, rendering_kw=dict(color="red"), use_latex=use_latex) + assert len(p.backend.series) == 2 f = p.fig ax = f.axes[0] assert isinstance(ax, matplotlib.axes.Axes) assert len(ax.get_lines()) == 2 - assert ax.get_lines()[0].get_label() == "sin(x)" + assert ax.get_lines()[0].get_label() == label_func(use_latex, sin(a * x)) assert ax.get_lines()[0].get_color() == "red" - assert ax.get_lines()[1].get_label() == "cos(x)" + assert ax.get_lines()[1].get_label() == label_func(use_latex, cos(b * x)) assert ax.get_lines()[1].get_color() == "red" - assert ax.get_xlabel() == "x" - assert ax.get_ylabel() == "f(x)" - p.close() - - p = make_plot_1(MB, rendering_kw=dict(color="red"), use_latex=True) - f = p.fig - ax = f.axes[0] - assert ax.get_lines()[0].get_label() == "$\\sin{\\left(x \\right)}$" - assert ax.get_xlabel() == "$x$" - assert ax.get_ylabel() == "$f\\left(x\\right)$" + assert ax.get_xlabel() == xlabel + assert ax.get_ylabel() == ylabel + p.backend.update_interactive({a: 2, b: 2}) + p.backend.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_plot_parametric(): # verify that the backends produce the expected results when # `plot_parametric()` is called and `rendering_kw` overrides the default # line settings - p = make_plot_parametric_1(MB, rendering_kw=dict(color="red")) - assert len(p.series) == 1 + p = make_test_plot_parametric(MB, rendering_kw=dict(color="red"), + use_cm=False) + assert len(p.backend.series) == 1 f = p.fig ax = f.axes[0] + assert ax.get_lines()[0].get_label() == "(cos(a*x), sin(b*x))" + assert ax.get_lines()[0].get_color() == "red" + p.backend.update_interactive({a: 2, b: 2}) + p.backend.close() + # parametric plot with use_cm=True -> LineCollection - assert len(ax.collections) == 1 - assert isinstance(ax.collections[0], matplotlib.collections.LineCollection) - assert f.axes[1].get_ylabel() == "x" - assert all(*(ax.collections[0].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0) - p.close() + p1 = make_test_plot_parametric(MB, rendering_kw={}, + use_cm=True) + p2 = make_test_plot_parametric(MB, rendering_kw=dict(cmap="autumn"), + use_cm=True) + f1, f2 = p1.fig, p2.fig + ax1, ax2 = f1.axes[0], f2.axes[0] + assert len(ax1.collections) == 1 + assert isinstance(ax1.collections[0], matplotlib.collections.LineCollection) + assert f1.axes[1].get_ylabel() == "x" + # TODO: how to test for different colormaps? + # assert not np.allclose( + # ax1.collections[0].get_colors(), + # ax2.collections[0].get_colors() + # ) + p1.backend.update_interactive({a: 2, b: 2}) + p2.backend.update_interactive({a: 2, b: 2}) + p1.backend.close() + p2.backend.close() -def test_plot3d_parametric_line(): +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +@pytest.mark.parametrize( + "use_latex", [False, True] +) +def test_plot3d_parametric_line(use_latex, label_func): # verify that the backends produce the expected results when # `plot3d_parametric_line()` is called and `rendering_kw` overrides the # default line settings - p = make_plot3d_parametric_line_1(MB, rendering_kw=dict(color="red")) - assert len(p.series) == 1 + p = make_test_plot3d_parametric_line( + MB, rendering_kw=dict(color="red"), use_latex=use_latex, use_cm=False) + assert len(p.backend.series) == 1 f = p.fig ax = f.axes[0] - assert len(ax.collections) == 1 + assert ax.get_lines()[0].get_label() == label_func( + use_latex, (cos(a * x), sin(b * x), x)) + assert ax.get_lines()[0].get_color() == "red" + p.backend.update_interactive({a: 2, b: 2}) + p.backend.close() + + p2 = make_test_plot3d_parametric_line( + MB, rendering_kw={}, use_latex=use_latex, use_cm=True) + p1 = make_test_plot3d_parametric_line( + MB, rendering_kw=dict(cmap="autumn"), use_latex=use_latex, use_cm=True) + f1, f2 = p1.fig, p2.fig + ax1, ax2 = f1.axes[0], f2.axes[0] + assert len(ax1.collections) == 1 assert isinstance( - ax.collections[0], - mpl_toolkits.mplot3d.art3d.Line3DCollection + ax1.collections[0], mpl_toolkits.mplot3d.art3d.Line3DCollection) + assert f1.axes[1].get_ylabel() == f2.axes[1].get_ylabel() == label_func( + use_latex, x ) - assert f.axes[1].get_ylabel() == "x" - assert all(*(ax.collections[0].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0) - p.close() + # TODO: how to test for different colormaps? + p1.backend.update_interactive({a: 2, b: 2}) + p2.backend.update_interactive({a: 2, b: 2}) + p1.backend.close() + p2.backend.close() -def test_plot3d(): +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +@pytest.mark.parametrize( + "use_latex, xl, yl, zl", [ + (False, "x", "y", "f(x, y)"), + (True, "$x$", "$y$", r"$f\left(x, y\right)$") + ] +) +def test_plot3d_1(use_latex, xl, yl, zl, label_func): # verify that the backends produce the expected results when # `plot3d()` is called and `rendering_kw` overrides the default surface # settings # use_cm=False will force to apply a default solid color to the mesh. # Here, I override that solid color with a custom color. - p = make_plot3d_1(MB, rendering_kw=dict(color="red")) - assert len(p.series) == 1 + p = make_test_plot3d(MB, rendering_kw=dict(color="red"), use_cm=False, + use_latex=use_latex) + assert len(p.backend.series) == 2 f = p.fig ax = f.axes[0] - assert len(ax.collections) == 1 - assert isinstance( - ax.collections[0], - mpl_toolkits.mplot3d.art3d.Poly3DCollection + assert len(ax.collections) == 2 + assert all(isinstance( + c, mpl_toolkits.mplot3d.art3d.Poly3DCollection) for c in ax.collections ) - # TODO: apparently, without showing the plot, the colors are not applied - # to a Poly3DCollection... - p.close() - + assert ax.get_xlabel() == xl + assert ax.get_ylabel() == yl + assert ax.get_zlabel() == zl + assert ( + ax.get_legend().legend_handles[0].get_label() + == label_func(use_latex, cos(a*x**2 + y**2)) + ) + assert ( + ax.get_legend().legend_handles[1].get_label() + == label_func(use_latex, sin(b*x**2 + y**2)) + ) + assert "cmap" not in p.backend.renderers[0].handles[0][1].keys() + # TODO: how to test for different colormaps? + p.backend.update_interactive({a: 2, b: 2}) + p.backend.close() -def test_plot3d_2(): - # verify that the backends uses string labels when `plot3d()` is called - # with `use_latex=False` and `use_cm=True` - p = make_plot3d_2(MB) - assert len(p.series) == 2 + p = make_test_plot3d(MB, rendering_kw=dict(cmap="autumn"), use_cm=True, + use_latex=use_latex) f = p.fig - ax = f.axes[0] - assert len(ax.collections) == 2 - assert ax.get_xlabel() == "x" - assert ax.get_ylabel() == "y" - assert ax.get_zlabel() == "f(x, y)" - assert len(f.axes) == 3 - assert f.axes[1].get_ylabel() == str(cos(x**2 + y**2)) - assert f.axes[2].get_ylabel() == str(sin(x**2 + y**2)) - p.close() + assert f.axes[1].get_ylabel() == label_func(use_latex, cos(a*x**2 + y**2)) + assert f.axes[2].get_ylabel() == label_func(use_latex, sin(b*x**2 + y**2)) + # TODO: how to test for different colormaps? + assert "cmap" in p.backend.renderers[0].handles[0][1].keys() + p.backend.update_interactive({a: 2, b: 2}) + p.backend.close() def test_plot3d_wireframe(): @@ -310,19 +323,31 @@ def test_plot3d_wireframe(): assert all(s.rendering_kw == {"lw": "0.5"} for s in p3.series[1:]) -def test_plot_contour(): +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +@pytest.mark.parametrize( + "use_latex, xl, yl", [ + (False, "x", "y"), + (True, "$x$", "$y$") + ] +) +def test_plot_contour(use_latex, xl, yl, label_func): # verify that the backends produce the expected results when # `plot_contour()` is called and `rendering_kw` overrides the default # surface settings - p = make_plot_contour_1(MB, rendering_kw=dict(cmap="jet")) - assert len(p.series) == 1 + p = make_test_plot_contour(MB, rendering_kw=dict(cmap="jet"), + use_latex=use_latex) + assert len(p.backend.series) == 1 f = p.fig ax = f.axes[0] assert len(ax.collections) > 0 - assert f.axes[1].get_ylabel() == str(cos(x**2 + y**2)) - # TODO: how to retrieve the colormap from a contour series????? - p.close() + assert ax.get_xlabel() == xl + assert ax.get_ylabel() == yl + assert "cmap" in p.backend.renderers[0].handles[0][1].keys() + assert f.axes[1].get_ylabel() == label_func(use_latex, cos(a*x**2 + y**2)) + # TODO: how to test for different colormaps? + p.backend.update_interactive({a: 2}) + p.backend.close() def test_plot_contour_is_filled(): @@ -339,120 +364,108 @@ def test_plot_contour_is_filled(): assert len(p2.renderers[0].handles[0][-1]) > 0 +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_plot_vector_2d_quivers(): # verify that the backends produce the expected results when # `plot_vector()` is called and `contour_kw`/`quiver_kw` overrides the # default settings - p = make_plot_vector_2d_quiver( + p = make_test_plot_vector_2d_quiver( MB, quiver_kw=dict(color="red"), contour_kw=dict(cmap="jet") ) - assert len(p.series) == 2 + assert len(p.backend.series) == 2 f = p.fig ax = f.axes[0] assert len(ax.collections) > 0 assert isinstance(ax.collections[-1], matplotlib.quiver.Quiver) assert f.axes[1].get_ylabel() == "Magnitude" - # TODO: how to retrieve the colormap from a contour series????? - p.close() + # TODO: how to test for different colormaps? + p.backend.update_interactive({a: 2}) + p.backend.close() -def test_plot_vector_2d_streamlines_custom_scalar_field(): - # verify that the backends produce the expected results when - # `plot_vector()` is called and `contour_kw`/`stream_kw` overrides the - # default settings - - p = make_plot_vector_2d_streamlines_1( - MB, stream_kw=dict(color="red"), contour_kw=dict(cmap="jet") - ) - assert len(p.series) == 2 - f = p.fig - ax = f.axes[0] - assert len(ax.collections) > 0 - assert isinstance(ax.collections[-1], matplotlib.collections.LineCollection) - assert f.axes[1].get_ylabel() == "x + y" - assert all(*(ax.collections[-1].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0) - p.close() - - -def test_plot_vector_2d_streamlines_custom_scalar_field_custom_label(): +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +@pytest.mark.parametrize( + "scalar, use_latex, expected_label", [ + (True, False, "Magnitude"), + (True, True, "Magnitude"), + (x + y, False, "x + y"), + (x + y, True, "$x + y$"), + ([(x + y), "test"], False, "test"), + ([(x + y), "test"], True, "test") + ] +) +def test_plot_vector_2d_streamlines_custom_scalar_field( + scalar, use_latex, expected_label +): # verify that the backends produce the expected results when # `plot_vector()` is called and `contour_kw`/`stream_kw` overrides the # default settings - p = make_plot_vector_2d_streamlines_2( - MB, stream_kw=dict(color="red"), contour_kw=dict(cmap="jet") + p = make_test_plot_vector_2d_streamlines( + MB, stream_kw=dict(color="red"), contour_kw=dict(cmap="jet"), + scalar=scalar, use_latex=use_latex ) - assert len(p.series) == 2 + assert len(p.backend.series) == 2 f = p.fig ax = f.axes[0] assert len(ax.collections) > 0 assert isinstance(ax.collections[-1], matplotlib.collections.LineCollection) - assert f.axes[1].get_ylabel() == "test" - assert all(*(ax.collections[-1].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0) - p.close() + assert f.axes[1].get_ylabel() == expected_label + # TODO: how to test for different colormaps? + raises(NotImplementedError, lambda :p.backend.update_interactive({a: 2})) + p.backend.close() -def test_plot_vector_2d_matplotlib(): +@pytest.mark.parametrize( + "scalar, streamlines, use_cm, n_series, n_axes, n_collections, " + "use_latex, label, greater", [ + # contours + quivers: 1 colorbar for the contours + (True, False, None, 2, 2, 1, False, "Magnitude", True), + (True, False, None, 2, 2, 1, True, "Magnitude", True), + # contours + streamlines: 1 colorbar for the contours + (True, True, None, 2, 2, 1, False, "Magnitude", True), + (True, True, None, 2, 2, 1, True, "Magnitude", True), + # only quivers: 1 colorbar for the quivers + (False, False, None, 1, 2, 1, False, "(x, y)", False), + (False, False, None, 1, 2, 1, True, r"$\left( x, \ y\right)$", False), + # only streamlines: 1 colorbar for the streamlines + (False, True, None, 1, 2, 1, False, "(x, y)", False), + (False, True, None, 1, 2, 1, True, r"$\left( x, \ y\right)$", False), + # only quivers with solid color + (False, False, False, 1, 1, 1, False, "", False), + (False, False, False, 1, 1, 1, True, "", False), + # only streamlines with solid color + (False, True, False, 1, 1, 1, False, "", False), + (False, True, False, 1, 1, 1, True, "", False), + ] +) +def test_plot_vector_2d_matplotlib( + scalar, streamlines, use_cm, n_series, n_axes, n_collections, + use_latex, label, greater +): # verify that when scalar=False, quivers/streamlines comes together with # a colorbar x, y = symbols("x, y") - def _plot_vector_1(scalar, streamlines, use_cm=None): - kwargs = {"scalar": scalar, "streamlines": streamlines} - if use_cm is not None: - kwargs["use_cm"] = use_cm - return plot_vector( - Matrix([x, y]), - (x, -5, 5), - (y, -4, 4), - backend=MB, - show=False, - use_latex=False, - n1=5, - n2=8, - **kwargs - ) + kwargs = {"scalar": scalar, "streamlines": streamlines} + if use_cm is not None: + kwargs["use_cm"] = use_cm + p = plot_vector( + Matrix([x, y]), (x, -5, 5), (y, -4, 4), + backend=MB, show=False, use_latex=use_latex, n1=5, n2=8, + **kwargs + ) # contours + quivers: 1 colorbar for the contours - p = _plot_vector_1(True, False) - assert len(p.series) == 2 - assert len(p.fig.axes) == 2 - assert len(p.fig.axes[0].collections) > 1 - assert p.fig.axes[1].get_ylabel() == "Magnitude" - - # contours + streamlines: 1 colorbar for the contours - p = _plot_vector_1(True, True) - assert len(p.series) == 2 - assert len(p.fig.axes) == 2 - assert len(p.fig.axes[0].collections) > 1 - assert p.fig.axes[1].get_ylabel() == "Magnitude" - - # only quivers: 1 colorbar for the quivers - p = _plot_vector_1(False, False) - assert len(p.series) == 1 - assert len(p.fig.axes) == 2 - assert len(p.fig.axes[0].collections) == 1 - assert p.fig.axes[1].get_ylabel() == "(x, y)" - - # only streamlines: 1 colorbar for the streamlines - p = _plot_vector_1(False, False) - assert len(p.series) == 1 - assert len(p.fig.axes) == 2 - assert len(p.fig.axes[0].collections) == 1 - assert p.fig.axes[1].get_ylabel() == "(x, y)" - - # only quivers with solid color - p = _plot_vector_1(False, False, False) - assert len(p.series) == 1 - assert len(p.fig.axes) == 1 - assert len(p.fig.axes[0].collections) == 1 - - # only streamlines with solid color - p = _plot_vector_1(False, False, False) - assert len(p.series) == 1 - assert len(p.fig.axes) == 1 - assert len(p.fig.axes[0].collections) == 1 + assert len(p.series) == n_series + assert len(p.fig.axes) == n_axes + if greater: + assert len(p.fig.axes[0].collections) > n_collections + else: + assert len(p.fig.axes[0].collections) == n_collections + idx = 1 if use_cm is None else 0 + assert p.fig.axes[idx].get_ylabel() == label def test_vector_2d_multiple_series(): @@ -474,13 +487,18 @@ def test_vector_2d_multiple_series(): assert len(g.ax.get_legend().legend_handles) == 2 -def test_plot_vector_3d_quivers(): +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_vector_3d_quivers(use_latex, label_func): # verify that the backends produce the expected results when # `plot_vector()` is called and `quiver_kw` overrides the # default settings - p = make_plot_vector_3d_quiver(MB, quiver_kw=dict(cmap="jet")) - assert len(p.series) == 1 + p = make_test_plot_vector_3d_quiver_streamlines( + MB, False, quiver_kw=dict(cmap="jet"), use_latex=use_latex) + assert len(p.backend.series) == 1 f = p.fig ax = f.axes[0] assert len(ax.collections) == 1 @@ -489,13 +507,14 @@ def test_plot_vector_3d_quivers(): mpl_toolkits.mplot3d.art3d.Line3DCollection ) assert ax.collections[0].cmap.name == "jet" - assert f.axes[1].get_ylabel() == str((z, y, x)) - p.close() + assert f.axes[1].get_ylabel() == label_func(use_latex, (a * z, y, x)) + p.backend.update_interactive({a: 2}) + p.backend.close() - p = make_plot_vector_3d_quiver( - MB, quiver_kw=dict(cmap=None, color="red"), use_cm=False + p = make_test_plot_vector_3d_quiver_streamlines( + MB, False, quiver_kw=dict(cmap=None, color="red"), use_cm=False ) - assert len(p.series) == 1 + assert len(p.backend.series) == 1 f = p.fig ax = f.axes[0] assert len(ax.collections) == 1 @@ -507,28 +526,39 @@ def test_plot_vector_3d_quivers(): ax.collections[0].get_color(), np.array([[1.0, 0.0, 0.0, 1.0]]) ) - p.close() + p.backend.update_interactive({a: 2}) + p.backend.close() @pytest.mark.skipif(vtk is None, reason="vtk is not installed") -def test_plot_vector_3d_streamlines(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_vector_3d_streamlines(use_latex, label_func): # verify that the backends produce the expected results when # `plot_vector()` is called and `stream_kw` overrides the # default settings - p = make_plot_vector_3d_streamlines_1(MB, stream_kw=dict()) - assert len(p.series) == 1 + p = make_test_plot_vector_3d_quiver_streamlines( + MB, True, stream_kw=dict(), use_latex=use_latex) + assert len(p.backend.series) == 1 f = p.fig ax = f.axes[0] assert len(ax.collections) == 1 assert isinstance(ax.collections[0], mpl_toolkits.mplot3d.art3d.Line3DCollection) - assert f.axes[1].get_ylabel() == str((z, y, x)) - p.close() + assert f.axes[1].get_ylabel() == label_func(use_latex, (a*z, y, x)) + raises( + NotImplementedError, + lambda: p.backend.update_interactive({a: 2}) + ) + p.backend.close() # test different combinations for streamlines: it should not raise errors - p = make_plot_vector_3d_streamlines_1(MB, stream_kw=dict(starts=True)) - p = make_plot_vector_3d_streamlines_1( - MB, + p = make_test_plot_vector_3d_quiver_streamlines( + MB, True, stream_kw=dict(starts=True)) + p.backend.close() + p = make_test_plot_vector_3d_quiver_streamlines( + MB, True, stream_kw=dict( starts={ "x": np.linspace(-5, 5, 10), @@ -537,46 +567,28 @@ def test_plot_vector_3d_streamlines(): } ), ) - p.close() + p.backend.close() # other keywords: it should not raise errors - p = make_plot_vector_3d_streamlines_1( - MB, stream_kw=dict(), kwargs=dict(use_cm=False) + p = make_test_plot_vector_3d_quiver_streamlines( + MB, True, stream_kw=dict(), use_cm=False ) f = p.fig ax = f.axes[0] assert len(ax.lines) == 1 assert ax.lines[0].get_color() == "#1f77b4" - p.close() - - -def test_plot_vector_2d_normalize(): - # verify that backends are capable of normalizing a vector field before - # plotting it. Since all backend are different from each other, let's test - # that data in the figures is different in the two cases normalize=True - # and normalize=False - - p1 = make_plot_vector_2d_normalize_1(MB, False) - p2 = make_plot_vector_2d_normalize_1(MB, True) - uu1 = p1.fig.axes[0].collections[0].U - vv1 = p1.fig.axes[0].collections[0].V - uu2 = p2.fig.axes[0].collections[0].U - vv2 = p2.fig.axes[0].collections[0].V - assert not np.allclose(uu1, uu2) - assert not np.allclose(vv1, vv2) - assert not np.allclose(np.sqrt(uu1**2 + vv1**2), 1) - assert np.allclose(np.sqrt(uu2**2 + vv2**2), 1) + p.backend.close() @pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") -def test_plot_vector_2d_normalize_interactive(): +def test_plot_vector_2d_normalize(): # verify that backends are capable of normalizing a vector field before # plotting it. Since all backend are different from each other, let's test # that data in the figures is different in the two cases normalize=True # and normalize=False - p1 = make_plot_vector_2d_normalize_2(MB, False) + p1 = make_test_plot_vector_2d_normalize(MB, False) p1.backend.update_interactive({u: 1.5}) - p2 = make_plot_vector_2d_normalize_2(MB, True) + p2 = make_test_plot_vector_2d_normalize(MB, True) p2.backend.update_interactive({u: 1.5}) uu1 = p1.backend.fig.axes[0].collections[0].U vv1 = p1.backend.fig.axes[0].collections[0].V @@ -586,129 +598,73 @@ def test_plot_vector_2d_normalize_interactive(): assert not np.allclose(vv1, vv2) assert not np.allclose(np.sqrt(uu1**2 + vv1**2), 1) assert np.allclose(np.sqrt(uu2**2 + vv2**2), 1) - - -@pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_vector_3d_normalize(): - # verify that backends are capable of normalizing a vector field before - # plotting it. Since all backend are different from each other, let's test - # that data in the figures is different in the two cases normalize=True - # and normalize=False - - p1 = make_plot_vector_3d_normalize_1(MB, False) - p2 = make_plot_vector_3d_normalize_1(MB, True) - seg1 = np.array(p1.fig.axes[0].collections[0].get_segments()) - seg2 = np.array(p2.fig.axes[0].collections[0].get_segments()) - # TODO: how can I test that these two quivers are different? - # assert not np.allclose(seg1, seg2) + p1.backend.close() + p2.backend.close() @pytest.mark.skipif(ct is None, reason="control is not installed") @pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_vector_3d_normalize_interactive(): +def test_plot_vector_3d_normalize(): # verify that backends are capable of normalizing a vector field before # plotting it. Since all backend are different from each other, let's test # that data in the figures is different in the two cases normalize=True # and normalize=False - p1 = make_plot_vector_3d_normalize_2(MB, False) + p1 = make_test_plot_vector_3d_normalize(MB, False) p1.backend.update_interactive({u: 1.5}) - p2 = make_plot_vector_3d_normalize_2(MB, True) + p2 = make_test_plot_vector_3d_normalize(MB, True) p2.backend.update_interactive({u: 1.5}) seg1 = np.array(p1.fig.axes[0].collections[0].get_segments()) seg2 = np.array(p2.fig.axes[0].collections[0].get_segments()) # TODO: how can I test that these two quivers are different? # assert not np.allclose(seg1, seg2) - - -def test_plot_vector_2d_quiver_color_func(): - # verify that color_func gets applied to 2D quivers - - p1 = make_plot_vector_2d_quiver_color_func_1(MB, None) - p2 = make_plot_vector_2d_quiver_color_func_1(MB, lambda x, y, u, v: x) - a1 = p1.fig.axes[0].collections[0].get_array() - a2 = p2.fig.axes[0].collections[0].get_array() - assert not np.allclose(a1, a2) + p1.backend.close() + p2.backend.close() @pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") -def test_plot_vector_2d_quiver_color_func_interactive(): +def test_plot_vector_2d_quiver_color_func(): # verify that color_func gets applied to 2D quivers - x, y, a = symbols("x y a") - _pv2 = lambda B, cf: plot_vector( - (-a * y, x), - (x, -2, 2), - (y, -2, 2), - scalar=False, - use_cm=True, - color_func=cf, - show=False, - backend=B, - n=3, - params={a: (1, 0, 2)}, - ) - - p1 = _pv2(MB, None) - p2 = _pv2(MB, lambda x, y, u, v: u) - p3 = _pv2(MB, lambda x, y, u, v: u) + p1 = make_test_plot_vector_2d_color_func(MB, False, None) + p2 = make_test_plot_vector_2d_color_func(MB, False, lambda x, y, u, v: u) + p3 = make_test_plot_vector_2d_color_func(MB, False, lambda x, y, u, v: u) p3.backend.update_interactive({a: 1.5}) a1 = p1.fig.axes[0].collections[0].get_array() a2 = p2.fig.axes[0].collections[0].get_array() a3 = p3.fig.axes[0].collections[0].get_array() assert (not np.allclose(a1, a2)) and (not np.allclose(a2, a3)) + p1.backend.close() + p2.backend.close() + p3.backend.close() def test_plot_vector_2d_streamline_color_func(): # verify that color_func gets applied to 2D streamlines - x, y, a = symbols("x, y, a") - - _pv = lambda cf: plot_vector( - (-y, x), - (x, -2, 2), - (y, -2, 2), - scalar=False, - streamlines=True, - use_cm=True, - color_func=cf, - show=False, - backend=MB, - n=3, - ) - # TODO: seems like streamline colors get applied only after the plot is # show... How do I perform this test? - p1 = _pv(None) - p2 = _pv(lambda x, y, u, v: x) + p1 = make_test_plot_vector_2d_color_func(MB, True, None) + p2 = make_test_plot_vector_2d_color_func(MB, True, lambda x, y, u, v: x) c1 = p1.fig.axes[0].collections[0].get_colors() c2 = p2.fig.axes[0].collections[0].get_colors() # assert not np.allclose(c1, c2) - - -def test_plot_vector_3d_quivers_color_func(): - # verify that color_func gets applied to 3D quivers - - # TODO: is it possible to check matplotlib colors without showing the plot? - p1 = make_plot_vector_3d_quiver_color_func_1(MB, None) - p2 = make_plot_vector_3d_quiver_color_func_1( - MB, lambda x, y, z, u, v, w: x) - p1.draw() - p2.draw() + p1.backend.close() + p2.backend.close() @pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_plot_vector_3d_quivers_color_func_interactive(): # verify that color_func gets applied to 3D quivers - p1 = make_plot_vector_3d_quiver_color_func_2(MB, None) - p2 = make_plot_vector_3d_quiver_color_func_2( - MB, lambda x, y, z, u, v, w: np.cos(u)) - p3 = make_plot_vector_3d_quiver_color_func_2( + p1 = make_test_plot_vector_3d_quiver_color_func(MB, None) + p2 = make_test_plot_vector_3d_quiver_color_func( MB, lambda x, y, z, u, v, w: np.cos(u)) - p1.backend.update_interactive({a: 0}) - p2.backend.update_interactive({a: 0}) - p3.backend.update_interactive({a: 2}) + # TODO: is it possible to check matplotlib colors without showing the plot? + p1.backend.update_interactive({a: 2}) + p2.backend.update_interactive({a: 2}) + p1.backend.close() + p2.backend.close() @pytest.mark.skipif(vtk is None, reason="vtk is not installed") @@ -716,11 +672,13 @@ def test_plot_vector_3d_streamlines_color_func(): # verify that color_func gets applied to 3D quivers # TODO: is it possible to check matplotlib colors without showing the plot? - p1 = make_plot_vector_3d_streamlines_color_func(MB, None) - p2 = make_plot_vector_3d_streamlines_color_func( + p1 = make_test_plot_vector_3d_streamlines_color_func(MB, None) + p2 = make_test_plot_vector_3d_streamlines_color_func( MB, lambda x, y, z, u, v, w: x) - p1.draw() - p2.draw() + p1.fig + p2.fig + raises(NotImplementedError, lambda: p1.backend.update_interactive({a: 2})) + raises(NotImplementedError, lambda: p2.backend.update_interactive({a: 2})) def test_plot_implicit_adaptive_true(): @@ -748,7 +706,7 @@ def test_plot_implicit_adaptive_false(): f = p.fig ax = f.axes[0] assert len(ax.collections) > 0 - # TODO: how to retrieve the colormap from a contour series????? + # TODO: how to test for different colormaps? p.close() @@ -769,12 +727,16 @@ def test_plot_implicit_multiple_expressions(): assert len(legend.get_lines()) > 0 -def test_plot_real_imag(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_real_imag(use_latex, label_func): # verify that the backends produce the expected results when # `plot_real_imag()` is called and `rendering_kw` overrides the default # settings - p = make_test_real_imag(MB, rendering_kw=dict(color="red")) + p = make_test_real_imag(MB, rendering_kw=dict(color="red"), + use_latex=use_latex) assert len(p.series) == 2 f = p.fig ax = f.axes[0] @@ -783,35 +745,49 @@ def test_plot_real_imag(): assert ax.get_lines()[0].get_color() == "red" assert ax.get_lines()[1].get_label() == "Im(sqrt(x))" assert ax.get_lines()[1].get_color() == "red" + assert ax.get_xlabel() == label_func(use_latex, x) + assert ax.get_ylabel() == r"$f\left(x\right)$" if use_latex else "f(x)" + p.close() -def test_plot_complex_1d(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_complex_1d(use_latex): # verify that the backends produce the expected results when # `plot_complex()` is called and `rendering_kw` overrides the default # settings - p = make_test_plot_complex_1d(MB, rendering_kw=dict(color="red")) + p = make_test_plot_complex_1d( + MB, rendering_kw=dict(cmap="autumn"), use_latex=use_latex) assert len(p.series) == 1 f = p.fig ax = f.axes[0] assert len(ax.collections) == 1 assert isinstance(ax.collections[0], matplotlib.collections.LineCollection) assert f.axes[1].get_ylabel() == "Arg(sqrt(x))" - assert all(*(ax.collections[0].get_color() - np.array([1.0, 0.0, 0.0, 1.0])) == 0) + assert f.axes[0].get_xlabel() == "Real" + assert f.axes[0].get_ylabel() == "Abs" + # TODO: how to test for different colormaps? p.close() -def test_plot_complex_2d(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_complex_2d(use_latex): # verify that the backends produce the expected results when # `plot_complex()` is called and `rendering_kw` overrides the default # settings - p = make_test_plot_complex_2d(MB, rendering_kw=dict()) + p = make_test_plot_complex_2d(MB, rendering_kw=dict(), use_latex=use_latex) assert len(p.series) == 1 f = p.fig ax = f.axes[0] assert len(ax.images) == 1 + assert f.axes[0].get_xlabel() == "Re" + assert f.axes[0].get_ylabel() == "Im" assert f.axes[1].get_ylabel() == "Argument" assert ax.images[0].get_extent() == [-5.0, 5.0, -5.0, 5.0] p.close() @@ -821,6 +797,8 @@ def test_plot_complex_2d(): f = p.fig ax = f.axes[0] assert len(ax.images) == 1 + assert f.axes[0].get_xlabel() == "Re" + assert f.axes[0].get_ylabel() == "Im" assert f.axes[1].get_ylabel() == "Argument" assert ax.images[0].get_extent() == [-6, 6, -7, 7] p.close() @@ -849,27 +827,19 @@ def test_plot_complex_list(): p.fig -def test_plot_list_is_filled_false(): +@pytest.mark.parametrize( + "is_filled", [True, False] +) +def test_plot_list_is_filled(is_filled): # verify that the backends produce the expected results when # `plot_list()` is called with `is_filled=False` - p = make_test_plot_list_is_filled_false(MB) - f = p.fig - ax = f.axes[0] - assert len(ax.lines) == 1 - assert ax.lines[0].get_markeredgecolor() != ax.lines[0].get_markerfacecolor() - p.close() - - -def test_plot_list_is_filled_true(): - # verify that the backends produce the expected results when - # `plot_list()` is called with `is_filled=True` - - p = make_test_plot_list_is_filled_true(MB) + p = make_test_plot_list_is_filled(MB, is_filled) f = p.fig ax = f.axes[0] assert len(ax.lines) == 1 - assert ax.lines[0].get_markeredgecolor() == ax.lines[0].get_markerfacecolor() + test = ax.lines[0].get_markeredgecolor() == ax.lines[0].get_markerfacecolor() + assert test is is_filled p.close() @@ -898,6 +868,7 @@ def test_plot_piecewise_single_series(): colors.add(l.get_color()) assert len(colors) == 1 assert not p.legend + p.close() def test_plot_piecewise_multiple_series(): @@ -912,6 +883,7 @@ def test_plot_piecewise_multiple_series(): for l in ax.lines: colors.add(l.get_color()) assert len(colors) == 2 + p.close() def test_plot_geometry_1(): @@ -930,19 +902,21 @@ def test_plot_geometry_1(): p.close() -def test_plot_geometry_2(): +@pytest.mark.parametrize( + "is_filled, n_lines, n_coll, n_patches, n_legend", [ + (False, 5, 1, 0, 5), + (True, 2, 1, 3, 5), + ] +) +def test_plot_geometry_2(is_filled, n_lines, n_coll, n_patches, n_legend): # verify that is_filled works correctly - p = make_test_plot_geometry_2(MB, False) - assert len(p.fig.axes[0].lines) == 5 - assert len(p.fig.axes[0].collections) == 1 - assert len(p.fig.axes[0].patches) == 0 - assert len(p.ax.get_legend().legend_handles) == 5 - p = make_test_plot_geometry_2(MB, True) - assert len(p.fig.axes[0].lines) == 2 - assert len(p.fig.axes[0].collections) == 1 - assert len(p.fig.axes[0].patches) == 3 - assert len(p.ax.get_legend().legend_handles) == 5 + p = make_test_plot_geometry_2(MB, is_filled) + assert len(p.fig.axes[0].lines) == n_lines + assert len(p.fig.axes[0].collections) == n_coll + assert len(p.fig.axes[0].patches) == n_patches + assert len(p.ax.get_legend().legend_handles) == n_legend + p.close() def test_plot_geometry_3d(): @@ -950,6 +924,7 @@ def test_plot_geometry_3d(): p = make_test_plot_geometry_3d(MB) p.draw() + p.close() def test_plot_geometry_rendering_kw(): @@ -961,6 +936,7 @@ def test_plot_geometry_rendering_kw(): assert p[0].rendering_kw == {"color": "red"} p.draw() assert p.ax.lines[0].get_color() == "red" + p.close() def test_save(): @@ -991,35 +967,20 @@ def test_save(): p.close() -@pytest.mark.skipif(ct is None, reason="control is not installed") -def test_vectors_3d_update_interactive(): - # Some backends do not support streamlines with iplot. Test that the - # backends raise error. - - p = make_test_vectors_3d_update_interactive(MB) - raises( - NotImplementedError, - lambda: p.backend.update_interactive({a: 2, b: 2, c: 2}) - ) - - -def test_aspect_ratio_2d_issue_11764(): +@pytest.mark.parametrize( + "aspect, expected", [ + ("auto", "auto"), + ((1, 1), 1), + ("equal", 1), + ] +) +def test_aspect_ratio_2d_issue_11764(aspect, expected): # verify that the backends apply the provided aspect ratio. # NOTE: read the backend docs to understand which options are available. - p = make_test_aspect_ratio_2d_issue_11764(MB) - assert p.aspect == "auto" - assert p.fig.axes[0].get_aspect() == "auto" - p.close() - - p = make_test_aspect_ratio_2d_issue_11764(MB, (1, 1)) - assert p.aspect == (1, 1) - assert p.fig.axes[0].get_aspect() == 1 - p.close() - - p = make_test_aspect_ratio_2d_issue_11764(MB, "equal") - assert p.aspect == "equal" - assert p.fig.axes[0].get_aspect() == 1 + p = make_test_aspect_ratio_2d_issue_11764(MB, aspect) + assert p.aspect == aspect + assert p.fig.axes[0].get_aspect() == expected p.close() @@ -1032,6 +993,7 @@ def test_aspect_ratio_3d(): p = make_test_aspect_ratio_3d(MB) assert p.aspect == "auto" + p.close() # Matplotlib 3D axis requires a string-valued aspect ratio # depending on the version, it raises one of the following errors @@ -1043,7 +1005,6 @@ def test_aspect_ratio_3d(): def test_plot_size(): # verify that the keyword `size` is doing it's job - # NOTE: K3DBackend doesn't support custom size x, y = symbols("x, y") @@ -1052,32 +1013,25 @@ def test_plot_size(): assert (s[0] == 8) and (s[1] == 4) p.close() - p = make_test_plot_size(MB, (10, 5)) - s = p.fig.get_size_inches() - assert (s[0] == 10) and (s[1] == 5) - p.close() - @pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_scale_lin_log(): +@pytest.mark.parametrize( + "xscale, yscale", [ + ("linear", "linear"), + ("log", "linear"), + ("linear", "log"), + ("log", "log"), + ] +) +def test_plot_scale_lin_log(xscale, yscale): # verify that backends are applying the correct scale to the axes # NOTE: none of the 3D libraries currently support log scale. x, y = symbols("x, y") - p = make_test_plot_scale_lin_log(MB, "linear", "linear") - assert p.fig.axes[0].get_xscale() == "linear" - assert p.fig.axes[0].get_yscale() == "linear" - p.close() - - p = make_test_plot_scale_lin_log(MB, "log", "linear") - assert p.fig.axes[0].get_xscale() == "log" - assert p.fig.axes[0].get_yscale() == "linear" - p.close() - - p = make_test_plot_scale_lin_log(MB, "linear", "log") - assert p.fig.axes[0].get_xscale() == "linear" - assert p.fig.axes[0].get_yscale() == "log" + p = make_test_plot_scale_lin_log(MB, xscale, yscale) + assert p.fig.axes[0].get_xscale() == xscale + assert p.fig.axes[0].get_yscale() == yscale p.close() @@ -1091,6 +1045,8 @@ def test_backend_latex_labels(): assert p2.xlabel == p2.fig.axes[0].get_xlabel() == "x_1^2" assert p1.ylabel == p1.fig.axes[0].get_ylabel() == "$f\\left(x^{2}_{1}\\right)$" assert p2.ylabel == p2.fig.axes[0].get_ylabel() == "f(x_1^2)" + p1.close() + p2.close() p1 = make_test_backend_latex_labels_2(MB, True) p2 = make_test_backend_latex_labels_2(MB, False) @@ -1102,180 +1058,11 @@ def test_backend_latex_labels(): assert p2.xlabel == p2.fig.axes[0].get_xlabel() == "x_1^2" assert p2.ylabel == p2.fig.axes[0].get_ylabel() == "x_2" assert p2.zlabel == p2.fig.axes[0].get_zlabel() == "f(x_1^2, x_2)" + p1.close() + p2.close() -def test_plot_use_latex(): - # verify that the backends produce the expected results when `plot()` - # is called and `rendering_kw` overrides the default line settings - - p = make_test_plot_use_latex(MB) - f = p.fig - ax = f.axes[0] - assert ax.get_lines()[0].get_label() == "$\\sin{\\left(x \\right)}$" - assert ax.get_lines()[1].get_label() == "$\\cos{\\left(x \\right)}$" - p.close() - - -def test_plot_parametric_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_parametric_use_latex(MB) - assert len(p.series) == 1 - f = p.fig - assert f.axes[1].get_ylabel() == "$x$" - p.close() - - -def test_plot_contour_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_contour_use_latex(MB) - assert len(p.series) == 1 - f = p.fig - assert f.axes[1].get_ylabel() == "$%s$" % latex(cos(x**2 + y**2)) - - -def test_plot3d_parametric_line_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot3d_parametric_line_use_latex(MB) - assert len(p.series) == 1 - f = p.fig - assert f.axes[1].get_ylabel() == "$x$" - p.close() - - -def test_plot3d_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot3d_use_latex(MB) - f = p.fig - assert len(f.axes) == 3 - assert f.axes[1].get_ylabel() == "$%s$" % latex(cos(x**2 + y**2)) - assert f.axes[2].get_ylabel() == "$%s$" % latex(sin(x**2 + y**2)) - p.close() - - -def test_plot_vector_2d_quivers_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_quivers_use_latex(MB) - f = p.fig - assert f.axes[1].get_ylabel() == "Magnitude" - p.close() - - -def test_plot_vector_2d_streamlines_custom_scalar_field_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_streamlines_custom_scalar_field_use_latex(MB) - f = p.fig - assert f.axes[1].get_ylabel() == "$x + y$" - p.close() - - -def test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex( - MB - ) - f = p.fig - assert f.axes[1].get_ylabel() == "test" - - -def test_plot_vector_2d_use_latex_colorbar(): - # verify that the colorbar uses latex label - - # contours + quivers: 1 colorbar for the contours - p = make_test_plot_vector_2d_use_latex_colorbar(MB, True, False) - assert p.fig.axes[1].get_ylabel() == "Magnitude" - p.close() - - # contours + streamlines: 1 colorbar for the contours - p = make_test_plot_vector_2d_use_latex_colorbar(MB, True, True) - assert p.fig.axes[1].get_ylabel() == "Magnitude" - p.close() - - # only quivers: 1 colorbar for the quivers - p = make_test_plot_vector_2d_use_latex_colorbar(MB, False, False) - assert p.fig.axes[1].get_ylabel() == "$\\left( x, \\ y\\right)$" - p.close() - - # only streamlines: 1 colorbar for the streamlines - p = make_test_plot_vector_2d_use_latex_colorbar(MB, False, True) - assert p.fig.axes[1].get_ylabel() == "$\\left( x, \\ y\\right)$" - p.close() - - -def test_plot_vector_3d_quivers_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_3d_quivers_use_latex(MB) - assert len(p.fig.axes) == 2 - assert p.fig.axes[1].get_ylabel() == "$\\left( z, \\ y, \\ x\\right)$" - p.close() - - -@pytest.mark.skipif(vtk is None, reason="vtk is not installed") -def test_plot_vector_3d_streamlines_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_3d_streamlines_use_latex(MB) - assert p.fig.axes[1].get_ylabel() == "$\\left( z, \\ y, \\ x\\right)$" - p.close() - - -@pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_complex_use_latex_1(): - # complex plot function should return the same result (for axis labels) - # wheter use_latex is True or False - - p = make_test_plot_complex_use_latex_1(MB) - assert p.fig.axes[0].get_xlabel() == "Real" - assert p.fig.axes[0].get_ylabel() == "Abs" - assert p.fig.axes[1].get_ylabel() == "Arg(cos(x) + I*sinh(x))" - p.close() - - -@pytest.mark.skipif(scipy is None, reason="scipy is not installed") -@pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_complex_use_latex_2(): - # complex plot function should return the same result (for axis labels) - # wheter use_latex is True or False - - p = make_test_plot_complex_use_latex_2(MB) - assert p.fig.axes[0].get_xlabel() == "Re" - assert p.fig.axes[0].get_ylabel() == "Im" - assert p.fig.axes[1].get_ylabel() == "Argument" - p.close() - - -def test_plot_real_imag_use_latex(): - # real/imag plot function should return the same result (for axis labels) - # wheter use_latex is True or False - - p = make_test_plot_real_imag_use_latex(MB) - assert p.fig.axes[0].get_xlabel() == "$x$" - assert p.fig.axes[0].get_ylabel() == r"$f\left(x\right)$" - assert p.fig.axes[0].lines[0].get_label() == "Re(sqrt(x))" - assert p.fig.axes[0].lines[1].get_label() == "Im(sqrt(x))" - p.close() - - -def test_plot3d_use_cm(): - # verify that use_cm produces the expected results on plot3d - - x, y = symbols("x, y") - p1 = make_test_plot3d_use_cm(MB, True) - p2 = make_test_plot3d_use_cm(MB, False) - p1.draw() - p2.draw() - assert "cmap" in p1.renderers[0].handles[0][1].keys() - assert "cmap" not in p2.renderers[0].handles[0][1].keys() - - -def test_plot3dupdate_interactive(): +def test_plot3d_update_interactive(): # verify that MB._update_interactive applies the original color/colormap # each time it gets called # Since matplotlib doesn't apply color/colormaps until the figure is shown, @@ -1303,6 +1090,7 @@ def test_plot3dupdate_interactive(): kw, _, _ = p.renderers[0].handles[0][1:] c2 = kw["color"] assert c1 == c2 + p.close() s = SurfaceOver2DRangeSeries( u * cos(x**2 + y**2), @@ -1324,6 +1112,7 @@ def test_plot3dupdate_interactive(): kw, _, _ = p.renderers[0].handles[0][1:] c2 = kw["cmap"] assert c1 == c2 + p.close() def test_plot_polar(): @@ -1334,10 +1123,12 @@ def test_plot_polar(): p1 = make_test_plot_polar(MB, False) assert not isinstance( p1.fig.axes[0], matplotlib.projections.polar.PolarAxes) + p1.close() # polar axis p1 = make_test_plot_polar(MB, True) assert isinstance(p1.fig.axes[0], matplotlib.projections.polar.PolarAxes) + p1.close() def test_plot_polar_use_cm(): @@ -1348,21 +1139,25 @@ def test_plot_polar_use_cm(): p = make_test_plot_polar_use_cm(MB, False, False) assert len(p.ax.lines) > 0 assert len(p.ax.collections) == 0 + p.close() # cartesian axis, with colormap p = make_test_plot_polar_use_cm(MB, False, True) assert len(p.ax.lines) == 0 assert len(p.ax.collections) > 0 + p.close() # polar axis, no colormap p = make_test_plot_polar_use_cm(MB, True, False) assert len(p.ax.lines) > 0 assert len(p.ax.collections) == 0 + p.close() # polar axis, with colormap p = make_test_plot_polar_use_cm(MB, True, True, lambda t: t) assert len(p.ax.lines) == 0 assert len(p.ax.collections) > 0 + p.close() def test_plot3d_implicit(): @@ -1371,62 +1166,27 @@ def test_plot3d_implicit(): raises(NotImplementedError, lambda: make_test_plot3d_implicit(MB).draw()) +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_surface_color_func(): # After the addition of `color_func`, `SurfaceOver2DRangeSeries` and # `ParametricSurfaceSeries` returns different elements. # Verify that backends do not raise errors when plotting surfaces and that # the color function is applied. - p1 = make_test_surface_color_func_1(MB, lambda x, y, z: z) - p1.draw() - p2 = make_test_surface_color_func_1( - MB, lambda x, y, z: np.sqrt(x**2 + y**2)) - p2.draw() - - p1 = make_test_surface_color_func_2(MB, lambda x, y, z, u, v: z) - p1.draw() - p2 = make_test_surface_color_func_2( - MB, lambda x, y, z, u, v: np.sqrt(x**2 + y**2) - ) - p2.draw() - - -def test_surface_interactive_color_func(): - # After the addition of `color_func`, `SurfaceInteractiveSeries` and - # `ParametricSurfaceInteractiveSeries` returns different elements. - # Verify that backends do not raise errors when updating surfaces and a - # color function is applied. - - p = make_test_surface_interactive_color_func(MB) - p.draw() - p.update_interactive({t: 2}) + p = make_test_surface_color_func(MB) + fig = p.fig + p.backend.update_interactive({t: 2}) + p.backend.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_line_color_func(): - # Verify that backends do not raise errors when plotting lines and that - # the color function is applied. - - p1 = make_test_line_color_func(MB, None) - p1.draw() - p2 = make_test_line_color_func(MB, lambda x, y: np.cos(x)) - p2.draw() - assert len(p1.fig.axes[0].lines) == 1 - assert isinstance( - p2.fig.axes[0].collections[0], matplotlib.collections.LineCollection - ) - assert np.allclose( - p2.fig.axes[0].collections[0].get_array(), - np.cos(np.linspace(-3, 3, 5)) - ) - - -def test_line_interactive_color_func(): # Verify that backends do not raise errors when updating lines and a # color function is applied. - p = make_test_line_interactive_color_func(MB) - p.draw() - p.update_interactive({t: 2}) + p = make_test_line_color_func(MB) + p.backend.draw() + p.backend.update_interactive({t: 2}) assert len(p.fig.axes[0].lines) == 1 assert isinstance( p.fig.axes[0].collections[0], matplotlib.collections.LineCollection @@ -1435,6 +1195,7 @@ def test_line_interactive_color_func(): p.fig.axes[0].collections[0].get_array(), np.cos(np.linspace(-3, 3, 5)) ) + p.backend.close() def test_line_color_plot(): @@ -1445,9 +1206,11 @@ def test_line_color_plot(): f = p.fig ax = f.axes[0] assert ax.get_lines()[0].get_color() == "red" + p.close() p = make_test_line_color_plot(MB, lambda x: -x) f = p.fig assert len(p.fig.axes) == 2 # there is a colorbar + p.close() def test_line_color_plot3d_parametric_line(): @@ -1485,269 +1248,182 @@ def test_label_after_plot_instantiation(): assert f.axes[0].lines[1].get_label() == "$b^{2}$" -@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") -def test_update_interactive(): - # quick round down of test to verify that _update_interactive doesn't - # raise errors +def test_min_install(): + # quick round down of test to verify that ordinay plots don't + # raise errors. Useful to test minimum installation of the module - u, v, x, y, z = symbols("u, v, x:z") + x, y, z = symbols("x:z") + options = dict(adaptive=False, n=5, backend=MB, show=False) + options2 = dict(n=5, backend=MB, show=False) - p = plot( - sin(u * x), - (x, -pi, pi), - adaptive=False, - n=5, - backend=MB, - show=False, - params={u: (1, 0, 2)}, - ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p = plot(sin(x), (x, -pi, pi), **options) + p.draw() + p.close() p = plot_parametric( - cos(u * x), - sin(u * x), - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=MB, - show=False, - params={u: (1, 0, 2)}, - use_cm=True, - is_point=False, + cos(x), sin(x), (x, 0, 2 * pi), + use_cm=True, is_point=False, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot_parametric( - cos(u * x), - sin(u * x), - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=MB, - show=False, - params={u: (1, 0, 2)}, - use_cm=True, - is_point=True, + cos(x), sin(x), (x, 0, 2 * pi), + use_cm=True, is_point=True, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) - - p = plot_parametric( - cos(u * x), - sin(u * x), - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=MB, - show=False, - params={u: (1, 0, 2)}, - use_cm=False, + p.draw() + p.close() + + p = plot_parametric( + cos(x), sin(x), (x, 0, 2 * pi), + use_cm=False, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot_implicit( - x**2 + y**2 - 4, - (x, -5, 5), - (y, -5, 5), - adaptive=False, - n=5, - show=False, - backend=MB, + x**2 + y**2 - 4, (x, -5, 5), (y, -5, 5), + **options ) + p.draw() + p.close() # points p = plot3d_parametric_line( - cos(u * x), - sin(x), - x, - (x, -pi, pi), - backend=MB, - is_point=True, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, + cos(x), sin(x), x, (x, -pi, pi), + is_point=True, **options2 ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() # line with colormap p = plot3d_parametric_line( - cos(u * x), - sin(x), - x, - (x, -pi, pi), - backend=MB, - is_point=False, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - use_cm=True, + cos(x), sin(x), x, (x, -pi, pi), + is_point=False, use_cm=True, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() # line with solid color p = plot3d_parametric_line( - cos(u * x), - sin(x), - x, - (x, -pi, pi), - backend=MB, - is_point=False, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - use_cm=False, + cos(x), sin(x), x, (x, -pi, pi), + is_point=False, use_cm=False, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot3d_parametric_line( - cos(u * x), - sin(x), - x, - (x, -pi, pi), - backend=MB, - is_point=False, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - use_cm=True, + cos(x), sin(x), x, (x, -pi, pi), + is_point=False, use_cm=True, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot3d( - cos(u * x**2 + y**2), - (x, -2, 2), - (y, -2, 2), - backend=MB, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, + cos(x**2 + y**2), (x, -2, 2), (y, -2, 2), + **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot_contour( - cos(u * x**2 + y**2), - (x, -2, 2), - (y, -2, 2), - backend=MB, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - is_filled=False, + cos(x**2 + y**2), (x, -2, 2), (y, -2, 2), + is_filled=False, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot_contour( - cos(u * x**2 + y**2), - (x, -2, 2), - (y, -2, 2), - backend=MB, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - is_filled=True, + cos(x**2 + y**2), (x, -2, 2), (y, -2, 2), + is_filled=True, **options ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() u, v = symbols("u, v") - fx = (1 + v / 2 * cos(u / 2)) * cos(x * u) - fy = (1 + v / 2 * cos(u / 2)) * sin(x * u) + fx = (1 + v / 2 * cos(u / 2)) * cos(u) + fy = (1 + v / 2 * cos(u / 2)) * sin(u) fz = v / 2 * sin(u / 2) p = plot3d_parametric_surface( - fx, - fy, - fz, - (u, 0, 2 * pi), - (v, -1, 1), - backend=MB, - use_cm=True, - n1=5, - n2=5, - show=False, - params={x: (1, 0, 2)}, + fx, fy, fz, (u, 0, 2 * pi), (v, -1, 1), + use_cm=True, **options2 ) - p.backend.draw() - p.backend.update_interactive({x: 2}) + p.draw() + p.close() p = plot_vector( - Matrix([-u * y, x]), - (x, -5, 5), - (y, -4, 4), - backend=MB, - n=4, - show=False, - params={u: (1, 0, 2)}, - scalar=True, + Matrix([-y, x]), (x, -5, 5), (y, -4, 4), + scalar=True, **options2 ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot_vector( - Matrix([-u * y, x]), - (x, -5, 5), - (y, -4, 4), - backend=MB, - n=4, - show=False, - params={u: (1, 0, 2)}, - scalar=False, + Matrix([-y, x]), (x, -5, 5), (y, -4, 4), + scalar=False, **options2 ) - p.backend.draw() - p.backend.update_interactive({u: 2}) + p.draw() + p.close() p = plot_vector( - Matrix([u * z, y, x]), - (x, -5, 5), - (y, -4, 4), - (z, -3, 3), - backend=MB, - n=4, - show=False, - params={u: (1, 0, 2)}, + Matrix([z, y, x]), (x, -5, 5), (y, -4, 4), (z, -3, 3), + **options2 + ) + p.draw() + p.close() + + p = plot_complex( + sqrt(x), (x, -5 - 5 * I, 5 + 5 * I), + threed=False, **options2 + ) + p.draw() + p.close() + + p = plot_complex( + sqrt(x), (x, -5 - 5 * I, 5 + 5 * I), + threed=True, use_cm=True, **options2 + ) + p.draw() + p.close() + + +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +def test_update_interactive(): + # quick round down of test to verify that update_interactive doesn't + # raise errors + + u, v, x, y, z = symbols("u, v, x:z") + + u, v = symbols("u, v") + fx = (1 + v / 2 * cos(u / 2)) * cos(x * u) + fy = (1 + v / 2 * cos(u / 2)) * sin(x * u) + fz = v / 2 * sin(u / 2) + p = plot3d_parametric_surface( + fx, fy, fz, (u, 0, 2 * pi), (v, -1, 1), + backend=MB, use_cm=True, n1=5, n2=5, show=False, + params={x: (1, 0, 2)}, ) p.backend.draw() - p.backend.update_interactive({u: 2}) + p.backend.update_interactive({x: 2}) + p.backend.close() p = plot_complex( - sqrt(u * x), - (x, -5 - 5 * I, 5 + 5 * I), - show=False, - backend=MB, - threed=False, - n=5, + sqrt(u * x), (x, -5 - 5 * I, 5 + 5 * I), + show=False, backend=MB, threed=False, n=5, params={u: (1, 0, 2)}, ) p.backend.draw() p.backend.update_interactive({u: 2}) + p.backend.close() p = plot_complex( - sqrt(u * x), - (x, -5 - 5 * I, 5 + 5 * I), - show=False, - backend=MB, - threed=True, - use_cm=True, - n=5, + sqrt(u * x), (x, -5 - 5 * I, 5 + 5 * I), + show=False, backend=MB, threed=True, use_cm=True, n=5, params={u: (1, 0, 2)}, ) p.backend.draw() p.backend.update_interactive({u: 2}) + p.backend.close() from sympy.geometry import Line as SymPyLine @@ -1764,6 +1440,7 @@ def test_update_interactive(): p.backend.draw() p.backend.update_interactive({u: 2}) p.backend.update_interactive({u: 3}) + p.backend.close() p = plot_geometry( SymPyLine((u, 2), (5, 4)), @@ -1778,6 +1455,7 @@ def test_update_interactive(): p.backend.draw() p.backend.update_interactive({u: 2}) p.backend.update_interactive({u: 3}) + p.backend.close() def test_generic_data_series(): @@ -1799,77 +1477,80 @@ def test_generic_data_series(): p.draw() -def test_axis_center(): +@pytest.mark.parametrize("ac", ["center", "auto", (0, 0)]) +def test_axis_center(ac): # verify that axis_center doesn't raise any errors x = symbols("x") - _plot = lambda ac: plot( + p = plot( sin(x), adaptive=False, n=5, backend=MB, show=False, axis_center=ac ) - - _plot("center").draw() - _plot("auto").draw() - _plot((0, 0)).draw() - - -def test_plot3d_list_use_cm_False(): - # verify that plot3d_list produces the expected results when no color map - # is required - - # solid color line - p = make_test_plot3d_list_use_cm_False(MB, False, False) - p.draw() - assert len(p.series) == 1 - assert len(p.ax.lines) == 1 - assert p.ax.lines[0].get_color() == "#1f77b4" - - # solid color markers with empty faces - p = make_test_plot3d_list_use_cm_False(MB, True, False) p.draw() - assert len(p.ax.collections) == 1 - assert p.ax.collections[0].get_facecolors().size == 0 - - # solid color markers with filled faces - p = make_test_plot3d_list_use_cm_False(MB, True, True) - p.draw() - assert len(p.ax.collections) == 1 - assert p.ax.collections[0].get_facecolors().size > 0 + p.close() -def test_plot3d_list_use_cm_color_func(): - # verify that use_cm=True and color_func do their job +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +def test_plot3d_list(): + # verify that no errors are raises while updating a plot3d_list - # line with colormap - # if color_func is not provided, the same parameter will be used - # for all points - p1 = make_test_plot3d_list_use_cm_color_func(MB, False, False, None) - p1.draw() - c1 = p1.ax.collections[0].get_array() - p2 = make_test_plot3d_list_use_cm_color_func( - MB, False, False, lambda x, y, z: x) - p2.draw() - c2 = p2.ax.collections[0].get_array() - assert not np.allclose(c1, c2) + p = make_test_plot3d_list(MB, False, None) + ax = p.backend.ax + d1 = p.backend[0].get_data() + assert len(ax.lines) == 1 + assert len(ax.collections) == 2 + c1, c2 = ax.collections + arr1 = c1.get_array() + # TODO: when use_cm=True, is_filled=False, this shouldn't happen. Bug. + assert len(c1.get_edgecolor()) == len(c1.get_facecolor()) + assert len(c2.get_edgecolor()) != len(c2.get_facecolor()) + p.backend.update_interactive({t: 1}) + p.backend.close() - # markers with empty faces - p1 = make_test_plot3d_list_use_cm_color_func(MB, True, False, None) - p1.draw() - c1 = p1.ax.collections[0].get_array() - p2 = make_test_plot3d_list_use_cm_color_func( - MB, False, False, lambda x, y, z: x) - p2.draw() - c2 = p2.ax.collections[0].get_array() - assert not np.allclose(c1, c2) + p = make_test_plot3d_list(MB, True, None) + ax = p.backend.ax + d2 = p.backend[0].get_data() + assert len(ax.lines) == 1 + assert len(ax.collections) == 2 + c1, c2 = ax.collections + arr2 = c1.get_array() + assert len(c1.get_edgecolor()) == len(c1.get_facecolor()) + assert len(c2.get_edgecolor()) == len(c2.get_facecolor()) + p.backend.update_interactive({t: 1}) + p.backend.close() + p = make_test_plot3d_list(MB, False, lambda x, y, z: x) + ax = p.backend.ax + d3 = p.backend[0].get_data() + assert len(ax.lines) == 1 + assert len(ax.collections) == 2 + c1, c2 = ax.collections + arr3 = c1.get_array() + # TODO: when use_cm=True, is_filled=False, this shouldn't happen. Bug. + assert len(c1.get_edgecolor()) == len(c1.get_facecolor()) + assert len(c2.get_edgecolor()) != len(c2.get_facecolor()) + p.backend.update_interactive({t: 1}) + p.backend.close() -@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") -def test_plot3d_list_interactive(): - # verify that no errors are raises while updating a plot3d_list + assert len(d1) != len(d3) + assert not np.allclose(arr1, arr3) - p = make_test_plot3d_list_interactive(MB) + p = make_test_plot3d_list(MB, True, lambda x, y, z: x) + ax = p.backend.ax + d4 = p.backend[0].get_data() + assert len(ax.lines) == 1 + assert len(ax.collections) == 2 + c1, c2 = ax.collections + arr4 = c1.get_array() + # TODO: when use_cm=True, is_filled=False, this shouldn't happen. Bug. + assert len(c1.get_edgecolor()) == len(c1.get_facecolor()) + assert len(c2.get_edgecolor()) == len(c2.get_facecolor()) p.backend.update_interactive({t: 1}) + p.backend.close() + + assert len(d2) != len(d4) + assert not np.allclose(arr2, arr4) def test_contour_and_3d(): @@ -1899,31 +1580,29 @@ def test_contour_and_3d(): p = p2 + p3 with warns(UserWarning, match="The following kwargs were not used by contour"): p.draw() + p.close() p = p1 + p3 raises(ValueError, lambda: p.draw()) + p.close() p = p1 + p2 + p3 raises(ValueError, lambda: p.draw()) + p.close() p = p2 + p1 + p3 raises(ValueError, lambda: p.draw()) + p.close() -# this test fails on matplotlib 3.4.2 -# guess they changed api in the newer releases -@pytest.mark.xfail +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_contour_show_clabels(): - p = make_test_contour_show_clabels_1(MB, False) - assert len(p.ax.texts) == 0 - - p = make_test_contour_show_clabels_1(MB, True) - assert len(p.ax.texts) > 0 - - p = make_test_contour_show_clabels_2(MB, False) - p.backend.update_interactive({Symbol("u"): 2}) + p = make_test_contour_show_clabels(MB, False) + p.backend.update_interactive({a: 2}) assert len(p.backend.ax.texts) == 0 + p.backend.close() - p = make_test_contour_show_clabels_2(MB, True) - p.backend.update_interactive({Symbol("u"): 2}) + p = make_test_contour_show_clabels(MB, True) + p.backend.update_interactive({a: 2}) assert len(p.backend.ax.texts) > 0 + p.backend.close() @pytest.mark.filterwarnings("ignore:The provided expression contains Boolean functions") @@ -1943,6 +1622,7 @@ def test_plot_implicit_legend_artists(): ) assert len(p.ax.get_legend().get_lines()) == 2 assert len(p.ax.get_legend().get_patches()) == 0 + p.close() # 2 expressions plotted with contourf -> 2 rectangles in legend p = plot_implicit( @@ -1956,6 +1636,7 @@ def test_plot_implicit_legend_artists(): ) assert len(p.ax.get_legend().get_lines()) == 0 assert len(p.ax.get_legend().get_patches()) == 2 + p.close() # two expressions plotted with fill -> 2 rectangles in legend p = plot_implicit( @@ -1968,6 +1649,7 @@ def test_plot_implicit_legend_artists(): ) assert len(p.ax.get_legend().get_lines()) == 0 assert len(p.ax.get_legend().get_patches()) == 2 + p.close() @pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") @@ -1985,12 +1667,15 @@ def test_color_func_expr(): # update the figure with new parameters: no errors should be raised p1.backend.update_interactive({u: 0.5}) + p1.backend.close() # interactive plots with streamlines are not implemented raises( NotImplementedError, lambda: p2.backend.update_interactive({u: 0.5}) ) + p2.backend.close() p3.backend.update_interactive({u: 0.5}) + p3.backend.close() def test_legend_plot_sum(): @@ -2001,20 +1686,26 @@ def test_legend_plot_sum(): # if legend is not specified, the resulting plot will show the legend p = make_test_legend_plot_sum_1(MB, None) assert len(p.ax.get_legend().legend_handles) == 3 + p.close() p = make_test_legend_plot_sum_1(MB, True) assert len(p.ax.get_legend().legend_handles) == 3 + p.close() # first plot has legend=False: output plot won't show the legend p = make_test_legend_plot_sum_1(MB, False) assert p.ax.get_legend() is None + p.close() # second case: legend is specified on the second plot # the resulting plot will always show the legend p = make_test_legend_plot_sum_2(MB, None) assert len(p.ax.get_legend().legend_handles) == 3 + p.close() p = make_test_legend_plot_sum_2(MB, True) assert len(p.ax.get_legend().legend_handles) == 3 + p.close() p = make_test_legend_plot_sum_2(MB, False) assert len(p.ax.get_legend().legend_handles) == 3 + p.close() # because plot_implicit creates custom proxy artists to show on the legend, # need to make sure that every legend artists is shown when combining @@ -2044,6 +1735,7 @@ def test_legend_plot_sum(): p3 = p1 + p2 handles = p3.ax.get_legend().legend_handles assert len(handles) == 2 + p3.close() def test_domain_coloring_2d(): @@ -2053,11 +1745,13 @@ def test_domain_coloring_2d(): _, _, _, _, img1a, _ = p1[0].get_data() img1b = p1.ax.images[0].get_array() assert np.allclose(img1a, img1b) + p1.close() p2 = make_test_domain_coloring_2d(MB, True) _, _, _, _, img2a, _ = p2[0].get_data() img2b = p2.ax.images[0].get_array() assert np.allclose(img2b, np.flip(np.flip(img2a, axis=0), axis=1)) + p2.close() @pytest.mark.filterwarnings("ignore::RuntimeWarning") @@ -2218,6 +1912,10 @@ def test_show_in_legend(): assert len(p2.ax.get_legend().legend_handles) == 2 assert len(p3.ax.get_legend().legend_handles) == 2 assert len(p4.ax.get_legend().legend_handles) == 2 + p1.close() + p2.close() + p3.close() + p4.close() @pytest.mark.filterwarnings("ignore::RuntimeWarning") @@ -2227,6 +1925,7 @@ def test_make_analytic_landscape_black_and_white(): p = make_test_analytic_landscape(MB) p.fig + p.close() def test_axis_limits(): @@ -2249,6 +1948,7 @@ def test_axis_limits(): show=False, ) p.draw() + p.close() def test_xaxis_inverted(): @@ -2258,9 +1958,11 @@ def test_xaxis_inverted(): x = symbols("x") p = plot(sin(x), (x, 0, 3), backend=MB, show=False, n=10) assert not p.ax.xaxis.get_inverted() + p.close() p = plot(sin(x), (x, 3, 0), backend=MB, show=False, n=10) assert p.ax.xaxis.get_inverted() + p.close() def test_detect_poles(): @@ -2268,12 +1970,14 @@ def test_detect_poles(): p = make_test_detect_poles(MB, False) p.draw() assert len(p.ax.lines) == 1 + p.close() # detection is done only with numerical data # only one line is visible p = make_test_detect_poles(MB, True) p.draw() assert len(p.ax.lines) == 1 + p.close() # detection is done only both with numerical data # and symbolic analysis. Multiple lines are visible @@ -2281,6 +1985,7 @@ def test_detect_poles(): p.draw() assert len(p.ax.lines) > 1 assert all(l.get_color() == "k" for l in p.ax.lines[1:]) + p.close() @pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") @@ -2318,22 +2023,21 @@ def test_detect_poles_interactive(): assert len(p.ax.lines) == 8 -def test_plot_riemann_sphere(): - p = make_test_plot_riemann_sphere(MB, True) - fig = p.fig - ax1 = fig.axes[0] - ax2 = fig.axes[1] - assert len(ax1.images) == len(ax2.images) == 1 - assert len(ax1.lines) == len(ax2.lines) == 3 - assert len(ax1.texts) == len(ax2.texts) == 4 - - p = make_test_plot_riemann_sphere(MB, False) +@pytest.mark.parametrize( + "annotate, n_imgs, n_lines, n_texts", [ + (True, 1, 3, 4), + (False, 1, 1, 0) + ] +) +def test_plot_riemann_sphere(annotate, n_imgs, n_lines, n_texts): + p = make_test_plot_riemann_sphere(MB, annotate) fig = p.fig ax1 = fig.axes[0] ax2 = fig.axes[1] - assert len(ax1.images) == len(ax2.images) == 1 - assert len(ax1.lines) == len(ax2.lines) == 1 - assert len(ax1.texts) == len(ax2.texts) == 0 + assert len(ax1.images) == len(ax2.images) == n_imgs + assert len(ax1.lines) == len(ax2.lines) == n_lines + assert len(ax1.texts) == len(ax2.texts) == n_texts + p.close() @pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") @@ -2347,6 +2051,7 @@ def test_parametric_texts(): assert p.backend.ax.get_title() == "y=1.5, z=2.000" assert p.backend.ax.get_xlabel() == "test y+z=3.50" assert p.backend.ax.get_ylabel() == "test z=2.00" + p.backend.close() a, b, p = make_test_parametric_texts_3d(MB) assert p.backend.ax.get_title() == "a=1.0, a+b=1.000" @@ -2358,23 +2063,27 @@ def test_parametric_texts(): assert p.backend.ax.get_xlabel() == "test a=1.50" assert p.backend.ax.get_ylabel() == "test b=2.00" assert p.backend.ax.get_zlabel() == "test a=1.50, b=2.00" + p.backend.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_arrow_2d(): a, b = symbols("a, b") p = make_test_arrow_2d(MB, "test", {"color": "r"}, True) - ax = p._backend.ax + ax = p.backend.ax assert isinstance(ax, Axes) assert len(ax.patches) == 1 assert len(ax.get_legend().legend_handles) == 1 assert ax.get_legend().legend_handles[0].get_label() == "$test$" assert ax.get_legend().legend_handles[0].get_color() == "r" - p._backend.update_interactive({a: 4, b: 5}) + p.backend.update_interactive({a: 4, b: 5}) + p.backend.close() p = make_test_arrow_2d(MB, "test", {"color": "r"}, False) - ax = p._backend.ax + ax = p.backend.ax assert len(ax.patches) == 1 assert ax.get_legend() is None + p.backend.close() def test_existing_figure_lines(): @@ -2396,6 +2105,7 @@ def test_existing_figure_lines(): assert ax.lines[0].get_color() == '#1f77b4' assert ax.lines[1].get_label() == "l2" assert ax.lines[1].get_color() == '#ff7f0e' + p.close() def test_existing_figure_surfaces(): @@ -2419,12 +2129,14 @@ def test_existing_figure_surfaces(): ax.collections[0].get_facecolors()[0], ax.collections[1].get_facecolors()[0] ) + p.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_arrow_3d(): a, b, c = symbols("a, b, c") p = make_test_arrow_3d(MB, "test", {"color": "r"}, True) - ax = p._backend.ax + ax = p.backend.ax assert isinstance(ax, mpl_toolkits.mplot3d.axes3d.Axes3D) assert len(ax.patches) == 1 assert len(ax.get_legend().legend_handles) == 1 @@ -2433,15 +2145,17 @@ def test_arrow_3d(): # only way to test if it renders what it's supposed to assert np.allclose(ax.patches[0]._xyz, [1, 2, 3]) assert np.allclose(ax.patches[0]._dxdydz, [4, 5, 6]) - p._backend.update_interactive({a: 4, b: 5, c: 6}) + p.backend.update_interactive({a: 4, b: 5, c: 6}) + p.backend.close() p = make_test_arrow_3d(MB, "test", {"color": "r"}, False) - ax = p._backend.ax + ax = p.backend.ax assert len(ax.patches) == 1 assert ax.get_legend() is None # only way to test if it renders what it's supposed to assert np.allclose(ax.patches[0]._xyz, [1, 2, 3]) assert np.allclose(ax.patches[0]._dxdydz, [4, 5, 6]) + p.backend.close() @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -2454,19 +2168,20 @@ def test_arrow_3d(): def test_plot_root_locus_1(sgrid, zgrid, n_lines, n_texts, instance): a = symbols("a") p = make_test_root_locus_1(MB, sgrid, zgrid) - assert isinstance(p._backend, MB) - assert len(p._backend.series) == 2 + assert isinstance(p.backend, MB) + assert len(p.backend.series) == 2 # NOTE: the backend is going to reorder data series such that grid # series are placed at the end. - assert isinstance(p._backend[0], RootLocusSeries) - assert isinstance(p._backend[1], instance) - ax = p._backend.ax + assert isinstance(p.backend[0], RootLocusSeries) + assert isinstance(p.backend[1], instance) + ax = p.backend.ax assert len(ax.lines) == n_lines assert ax.get_legend() is None assert len(ax.texts) == n_texts # number of sgrid labels on the plot line_colors = {'#1f77b4', '0.75'} assert all(l.get_color() in line_colors for l in ax.lines) - p._backend.update_interactive({a: 2}) + p.backend.update_interactive({a: 2}) + p.backend.close() @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -2485,10 +2200,10 @@ def test_plot_root_locus_2(): assert len(p.ax.texts) == 10 # number of sgrid labels on the plot line_colors = {'#1f77b4', '#ff7f0e', '0.75'} assert all(l.get_color() in line_colors for l in ax.lines) - p.update_interactive({}) - + p.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") @pytest.mark.parametrize( "sgrid, zgrid, T, is_filled", [ (True, False, None, True), @@ -2503,16 +2218,18 @@ def test_plot_pole_zero(sgrid, zgrid, T, is_filled): p = make_test_plot_pole_zero(MB, sgrid=sgrid, zgrid=zgrid, T=T, is_filled=is_filled) fig = p.fig - p._backend.update_interactive({a: 2}) + p.backend.update_interactive({a: 2}) + p.backend.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") @pytest.mark.filterwarnings("ignore::UserWarning") def test_plot_poles_zeros_sgrid(): # verify that SGridLineSeries is rendered with "proper" axis limits a = symbols("a") p = make_test_poles_zeros_sgrid(MB) - ax = p._backend.ax + ax = p.backend.ax xlim = ax.get_xlim() ylim = ax.get_ylim() assert (xlim is not None) and (ylim is not None) @@ -2520,7 +2237,8 @@ def test_plot_poles_zeros_sgrid(): # the code for better positioning the grid... assert xlim[0] > -5 and xlim[1] < 2 assert ylim[0] > -5 and ylim[1] < 5 - p._backend.update_interactive({a: 2}) + p.backend.update_interactive({a: 2}) + p.backend.close() @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -2528,7 +2246,7 @@ def test_plot_root_locus_sgrid(): # verify that SGridLineSeries is rendered with "proper" axis limits p = make_test_root_locus_1(MB, True, False) - ax = p._backend.ax + ax = p.backend.ax xlim = ax.get_xlim() ylim = ax.get_ylim() assert (xlim is not None) and (ylim is not None) @@ -2536,6 +2254,7 @@ def test_plot_root_locus_sgrid(): # the code for better positioning the grid... assert xlim[0] > -5 and xlim[1] < 2 assert ylim[0] > -5 and ylim[1] < 5 + p.backend.close() @pytest.mark.parametrize( @@ -2552,6 +2271,7 @@ def test_ngrid(cl_mags, cl_phases, label_cl_phases, n_lines, n_texts): ax = p.ax assert len(ax.lines) == n_lines assert len(ax.texts) == n_texts + p.close() @pytest.mark.parametrize( @@ -2583,11 +2303,14 @@ def test_sgrid(xi, wn, tp, ts, auto, show_control_axis, params, n_lines, n_texts kw["params"] = params p = make_test_sgrid(MB, xi, wn, tp, ts, auto, show_control_axis, **kw) - ax = p._backend.ax if params else p.ax + ax = p.backend.ax if params else p.ax assert len(ax.lines) == n_lines assert len(ax.texts) == n_texts if params: - p._backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) + p.backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) + p.backend.close() + else: + p.close() @pytest.mark.parametrize( @@ -2617,11 +2340,14 @@ def test_zgrid(xi, wn, tp, ts, show_control_axis, params, n_lines, n_texts): kw["params"] = params p = make_test_zgrid(MB, xi, wn, tp, ts, show_control_axis, **kw) - ax = p._backend.ax if params else p.ax + ax = p.backend.ax if params else p.ax assert len(ax.lines) == n_lines assert len(ax.texts) == n_texts if params: - p._backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) + p.backend.update_interactive({x: 0.75, y: 0.8, z: 0.85}) + p.backend.close() + else: + p.close() # On Github, it fails on the minimum installation version, @@ -2641,6 +2367,7 @@ def test_matplotlib_update_ranges(update_event, num_callbacks): if update_event: p._update_axis_limits("button_release_event") + p.close() p = plot_contour(cos(x**2+y**2), (x, -pi, pi), (y, -pi, pi), n=10, backend=MB, show=False, update_event=update_event) @@ -2648,6 +2375,7 @@ def test_matplotlib_update_ranges(update_event, num_callbacks): if update_event: p._update_axis_limits("button_release_event") + p.close() @pytest.mark.parametrize( @@ -2663,6 +2391,7 @@ def test_mcircles(mag, n_lines, n_labels): ax = p.ax assert len(ax.lines) == n_lines assert len(ax.texts) == n_labels + p.close() @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -2690,6 +2419,7 @@ def test_plot_nyquist_matplotlib( assert len(ax.lines) == n_lines assert len(ax.patches) == n_patches assert len(ax.texts) == n_texts + p.close() @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -2713,6 +2443,7 @@ def test_plot_nyquist_matplotlib_linestyles(primary_style, mirror_style): ax = p.ax else: raises(ValueError, lambda: p.ax) + p.close() @pytest.mark.skipif(ct is None, reason="control is not installed") @@ -2729,6 +2460,7 @@ def test_plot_nyquist_matplotlib_interactive(): ) ax = pl.backend.ax # force first draw pl.backend.update_interactive({a: 2}) # update with new value + pl.backend.close() def test_plot_nichols(): @@ -2740,12 +2472,14 @@ def test_plot_nichols(): ax = p.ax assert len(ax.lines) > 2 assert len(ax.texts) > 0 + p.close() # no nichols grid lines p = plot_nichols(tf, ngrid=False, show=False, n=10) ax = p.ax assert len(ax.lines) == 1 assert len(ax.texts) == 0 + p.close() @pytest.mark.parametrize( @@ -2764,8 +2498,10 @@ def test_plot_nichols_arrows(arrows, n_arrows): p = plot_nichols(tf, ngrid=False, show=False, n=10, arrows=arrows) ax = p.ax assert len(ax.patches) == n_arrows + p.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") @pytest.mark.parametrize( "scatter, use_cm, n_lines, n_collections", [ (False, False, 1, 0), @@ -2782,13 +2518,15 @@ def test_plot_nichols_lines_scatter(scatter, use_cm, n_lines, n_collections): # with nichols grid lines p = plot_nichols(tf, ngrid=False, show=False, n=10, backend=MB, scatter=scatter, use_cm=use_cm, params={a: (5, 0, 10)}) - ax = p._backend.ax + ax = p.backend.ax assert len(ax.lines) == n_lines assert len(ax.collections) == n_collections - p._backend.update_interactive({a: 6}) + p.backend.update_interactive({a: 6}) + p.backend.close() @pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") +@pytest.mark.filterwarnings("ignore::UserWarning") def test_plot_step_response(): # this should not raise any errors during updates @@ -2810,17 +2548,20 @@ def test_plot_step_response(): backend=MB, n=10, show=False ) fig = p.fig - p._backend.update_interactive({ + p.backend.update_interactive({ a: 4, b: 11, c:6, d: 8, e: 18, f: 5, g: 20 }) + p.backend.close() +@pytest.mark.skipif(ipy is None, reason="ipywidgets is not installed") def test_hvlines(): a, b = symbols("a, b") p = make_test_hvlines(MB) - ax = p._backend.ax + ax = p.backend.ax assert len(ax.lines) == 2 assert not np.allclose( ax.lines[0].get_data(), ax.lines[1].get_data() ) - p._backend.update_interactive({a: 3, b: 4}) + p.backend.update_interactive({a: 3, b: 4}) + p.backend.close() diff --git a/tests/backends/test_plotly.py b/tests/backends/test_plotly.py index 514ef5ba..8bc3be55 100644 --- a/tests/backends/test_plotly.py +++ b/tests/backends/test_plotly.py @@ -20,11 +20,10 @@ from sympy.abc import x, y, z, u, t, a, b, c from .make_tests import ( custom_colorloop_1, - make_plot_1, - make_plot_parametric_1, - make_plot3d_parametric_line_1, - make_plot3d_1, - make_plot3d_2, + make_test_plot, + make_test_plot_parametric, + make_test_plot3d_parametric_line, + make_test_plot3d, make_plot3d_wireframe_1, make_plot3d_wireframe_2, make_plot3d_wireframe_3, @@ -32,24 +31,21 @@ make_plot3d_wireframe_5, make_plot3d_parametric_surface_wireframe_1, make_plot3d_parametric_surface_wireframe_2, - make_plot_contour_1, + make_test_plot3d_use_cm, + make_test_plot_contour, make_plot_contour_is_filled, - make_plot_vector_2d_quiver, - make_plot_vector_2d_streamlines_1, - make_plot_vector_2d_streamlines_2, - make_plot_vector_3d_quiver, - make_plot_vector_3d_streamlines_1, - make_plot_vector_2d_normalize_1, - make_plot_vector_2d_normalize_2, - make_plot_vector_3d_normalize_1, - make_plot_vector_3d_normalize_2, + make_test_plot_vector_2d_quiver, + make_test_plot_vector_2d_streamlines, + make_test_plot_vector_3d_quiver_streamlines, + make_test_plot_vector_2d_normalize, + make_test_plot_vector_3d_normalize, + make_test_plot_vector_2d_color_func, make_test_plot_implicit_adaptive_true, make_test_plot_implicit_adaptive_false, make_test_plot_complex_1d, make_test_plot_complex_2d, make_test_plot_complex_3d, - make_test_plot_list_is_filled_false, - make_test_plot_list_is_filled_true, + make_test_plot_list_is_filled, make_test_plot_piecewise_single_series, make_test_plot_piecewise_multiple_series, make_test_plot_geometry_1, @@ -61,34 +57,15 @@ make_test_plot_scale_lin_log, make_test_backend_latex_labels_1, make_test_backend_latex_labels_2, - make_test_plot_use_latex, - make_test_plot_parametric_use_latex, - make_test_plot_contour_use_latex, - make_test_plot_vector_2d_quivers_use_latex, - make_test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex, - make_test_plot_vector_2d_streamlines_custom_scalar_field_use_latex, - make_test_plot_vector_2d_use_latex_colorbar, - make_test_plot_vector_3d_quivers_use_latex, - make_test_plot_vector_3d_streamlines_use_latex, - make_test_plot_complex_use_latex_1, - make_test_plot_complex_use_latex_2, - make_test_plot_real_imag_use_latex, - make_test_plot3d_use_cm, make_test_plot_polar, make_test_plot_polar_use_cm, make_test_plot3d_implicit, - make_test_surface_color_func_1, - make_test_surface_color_func_2, - make_test_surface_interactive_color_func, - make_test_line_interactive_color_func, + make_test_surface_color_func, make_test_line_color_plot, make_test_line_color_plot3d_parametric_line, make_test_surface_color_plot3d, - make_test_plot3d_list_use_cm_False, - make_test_plot3d_list_use_cm_color_func, - make_test_plot3d_list_interactive, - make_test_contour_show_clabels_1, - make_test_contour_show_clabels_2, + make_test_plot3d_list, + make_test_contour_show_clabels, make_test_color_func_expr_2, make_test_legend_plot_sum_1, make_test_legend_plot_sum_2, @@ -102,9 +79,6 @@ make_test_parametric_texts_2d, make_test_parametric_texts_3d, make_test_line_color_func, - make_test_plot3d_parametric_line_use_latex, - make_test_plot3d_use_latex, - make_test_vectors_3d_update_interactive, make_test_plot_list_color_func, make_test_real_imag, make_test_arrow_2d, @@ -126,18 +100,6 @@ class PBchild(PB): colorloop = ["red", "green", "blue"] -def test_colorloop_colormaps(): - # verify that backends exposes important class attributes enabling - # automatic coloring - - assert hasattr(PB, "colorloop") - assert isinstance(PB.colorloop, (list, tuple)) - assert hasattr(PB, "colormaps") - assert isinstance(PB.colormaps, (list, tuple)) - assert hasattr(PB, "quivers_colors") - assert isinstance(PB.quivers_colors, (list, tuple)) - - def test_custom_colorloop(): # verify that it is possible to modify the backend's class attributes # in order to change custom coloring @@ -155,31 +117,33 @@ def test_custom_colorloop(): assert len(set([d["line"]["color"] for d in f2.data])) == 3 -def test_plot(): +@pytest.mark.parametrize( + "use_latex, xlabel, ylabel", [ + (False, "x", "f(x)"), + (True, "$x$", "$f\\left(x\\right)$") + ] +) +def test_plot_1(use_latex, xlabel, ylabel, label_func): # verify that the backends produce the expected results when `plot()` # is called and `rendering_kw` overrides the default line settings - p = make_plot_1(PB, rendering_kw=dict(line_color="red")) - assert len(p.series) == 2 + p = make_test_plot(PB, rendering_kw=dict(line_color="red"), + use_latex=use_latex) + assert len(p.backend.series) == 2 f = p.fig assert len(f.data) == 2 assert isinstance(f.data[0], go.Scatter) - assert f.data[0]["name"] == "sin(x)" + assert f.data[0]["name"] == label_func(use_latex, sin(a * x)) assert f.data[0]["line"]["color"] == "red" assert isinstance(f.data[1], go.Scatter) - assert f.data[1]["name"] == "cos(x)" + assert f.data[1]["name"] == label_func(use_latex, cos(b * x)) assert f.data[1]["line"]["color"] == "red" assert f.layout["showlegend"] is True # PB separates the data generation from the layout creation. Make sure # the layout has been processed - assert f.layout["xaxis"]["title"]["text"] == "x" - assert f.layout["yaxis"]["title"]["text"] == "f(x)" - - p = make_plot_1(PB, rendering_kw=dict(line_color="red"), use_latex=True) - f = p.fig - assert f.data[0]["name"] == "$\\sin{\\left(x \\right)}$" - assert f.layout["xaxis"]["title"]["text"] == "$x$" - assert f.layout["yaxis"]["title"]["text"] == "$f\\left(x\\right)$" + assert f.layout["xaxis"]["title"]["text"] == xlabel + assert f.layout["yaxis"]["title"]["text"] == ylabel + p.backend.update_interactive({a: 2, b: 2}) def test_plot_parametric(): @@ -187,66 +151,94 @@ def test_plot_parametric(): # `plot_parametric()` is called and `rendering_kw` overrides the default # line settings - p = make_plot_parametric_1(PB, rendering_kw=dict(line_color="red")) - assert len(p.series) == 1 + p = make_test_plot_parametric(PB, rendering_kw=dict(line_color="red"), + use_cm=False) + assert len(p.backend.series) == 1 f = p.fig assert len(f.data) == 1 assert isinstance(f.data[0], go.Scatter) - assert f.data[0]["name"] == "x" + assert f.data[0]["name"] == "(cos(a*x), sin(b*x))" assert f.data[0]["line"]["color"] == "red" + assert f.data[0]["marker"]["colorbar"]["title"]["text"] is None + assert f.data[0]["marker"]["colorscale"] is None + p.backend.update_interactive({a: 2, b: 2}) + + p = make_test_plot_parametric(PB, rendering_kw=dict(line_color="red"), + use_cm=True) + assert len(p.backend.series) == 1 + f = p.fig + assert f.data[0]["name"] == "x" assert f.data[0]["marker"]["colorbar"]["title"]["text"] == "x" + p.backend.update_interactive({a: 2, b: 2}) -def test_plot3d_parametric_line(): +@pytest.mark.parametrize( + "use_latex", [False, True] +) +def test_plot3d_parametric_line(use_latex, label_func): # verify that the backends produce the expected results when # `plot3d_parametric_line()` is called and `rendering_kw` overrides the # default line settings - p = make_plot3d_parametric_line_1(PB, rendering_kw=dict(line_color="red")) - assert len(p.series) == 1 + p = make_test_plot3d_parametric_line(PB, + rendering_kw=dict(line_color="red"), + use_latex=use_latex, use_cm=False) + assert len(p.backend.series) == 1 f = p.fig assert len(f.data) == 1 assert isinstance(f.data[0], go.Scatter3d) assert f.data[0]["line"]["color"] == "red" - assert f.data[0]["name"] == "x" - assert f.data[0]["line"]["colorbar"]["title"]["text"] == "x" - - -def test_plot3d(): + assert f.data[0]["name"] == label_func( + use_latex, (cos(a * x), sin(b * x), x)) + assert f.data[0]["line"]["colorbar"]["title"]["text"] is None + p.backend.update_interactive({a: 2, b: 2}) + + p = make_test_plot3d_parametric_line(PB, + rendering_kw=dict(line_color="red"), + use_latex=use_latex, use_cm=True) + assert len(p.backend.series) == 1 + f = p.fig + assert len(f.data) == 1 + assert isinstance(f.data[0], go.Scatter3d) + assert f.data[0]["line"]["color"] == "red" + assert f.data[0]["name"] == label_func( + use_latex, x) + assert f.data[0]["line"]["colorbar"]["title"]["text"] == label_func( + use_latex, x) + p.backend.update_interactive({a: 2, b: 2}) + + +@pytest.mark.parametrize( + "use_latex, xl, yl, zl", [ + (False, "x", "y", "f(x, y)"), + (True, "$x$", "$y$", r"$f\left(x, y\right)$") + ] +) +def test_plot3d_1(use_latex, xl, yl, zl, label_func): # verify that the backends produce the expected results when # `plot3d()` is called and `rendering_kw` overrides the default surface # settings - p = make_plot3d_1( - PB, rendering_kw=dict(colorscale=[[0, "cyan"], [1, "cyan"]])) - assert len(p.series) == 1 + p = make_test_plot3d( + PB, rendering_kw=dict(colorscale=[[0, "cyan"], [1, "cyan"]]), + use_cm=False, use_latex=use_latex) + assert len(p.backend.series) == 2 f = p.fig - assert len(f.data) == 1 - assert isinstance(f.data[0], go.Surface) - assert f.data[0]["name"] == "cos(x**2 + y**2)" + assert len(f.data) == 2 + assert p.fig.layout.scene.xaxis.title.text == xl + assert p.fig.layout.scene.yaxis.title.text == yl + assert p.fig.layout.scene.zaxis.title.text == zl + assert all(isinstance(d, go.Surface) for d in f.data) + assert f.data[0]["name"] == label_func(use_latex, cos(a*x**2 + y**2)) + assert f.data[1]["name"] == label_func(use_latex, sin(b*x**2 + y**2)) assert not f.data[0]["showscale"] + assert not f.data[1]["showscale"] assert f.data[0]["colorscale"] == ((0, "cyan"), (1, "cyan")) - assert not f.layout["showlegend"] - assert f.data[0]["colorbar"]["title"]["text"] == "cos(x**2 + y**2)" - - -def test_plot3d_2(): - # verify that the backends uses string labels when `plot3d()` is called - # with `use_latex=False` and `use_cm=True` - - p = make_plot3d_2(PB) - assert len(p.series) == 2 - f = p.fig - assert len(f.data) == 2 - assert p.fig.layout.scene.xaxis.title.text == "x" - assert p.fig.layout.scene.yaxis.title.text == "y" - assert p.fig.layout.scene.zaxis.title.text == "f(x, y)" - assert f.data[0].colorbar.title.text == str(cos(x**2 + y**2)) - assert f.data[1].colorbar.title.text == str(sin(x**2 + y**2)) - assert f.data[0].name == str(cos(x**2 + y**2)) - assert f.data[1].name == str(sin(x**2 + y**2)) - assert f.data[0]["showscale"] - assert f.layout["showlegend"] is False + assert f.data[1]["colorscale"] == ((0, "cyan"), (1, "cyan")) + assert f.layout["showlegend"] + assert f.data[0]["colorbar"]["title"]["text"] == label_func(use_latex, cos(a*x**2 + y**2)) + assert f.data[1]["colorbar"]["title"]["text"] == label_func(use_latex, sin(b*x**2 + y**2)) + p.backend.update_interactive({a: 2, b: 2}) def test_plot3d_wireframe(): @@ -392,19 +384,30 @@ def test_plot3d_parametric_surface_wireframe_lambda_function(): ) -def test_plot_contour(): +@pytest.mark.parametrize( + "use_latex, xl, yl", [ + (False, "x", "y"), + (True, "$x$", "$y$") + ] +) +def test_plot_contour(use_latex, xl, yl, label_func): # verify that the backends produce the expected results when # `plot_contour()` is called and `rendering_kw` overrides the default # surface settings - p = make_plot_contour_1( - PB, rendering_kw=dict(contours=dict(coloring="lines"))) - assert len(p.series) == 1 + p = make_test_plot_contour( + PB, rendering_kw=dict(contours=dict(coloring="lines")), + use_latex=use_latex) + assert len(p.backend.series) == 1 f = p.fig assert len(f.data) == 1 assert isinstance(f.data[0], go.Contour) assert f.data[0]["contours"]["coloring"] == "lines" - assert f.data[0]["colorbar"]["title"]["text"] == str(cos(x**2 + y**2)) + assert f.data[0]["colorbar"]["title"]["text"] == label_func( + use_latex, cos(a*x**2 + y**2)) + assert f.layout["xaxis"]["title"]["text"] == xl + assert f.layout["yaxis"]["title"]["text"] == yl + p.backend.update_interactive({a: 2}) def test_plot_contour_is_filled(): @@ -426,12 +429,12 @@ def test_plot_vector_2d_quivers(): # `plot_vector()` is called and `contour_kw`/`quiver_kw` overrides the # default settings - p = make_plot_vector_2d_quiver( + p = make_test_plot_vector_2d_quiver( PB, quiver_kw=dict(line_color="red"), contour_kw=dict(contours=dict(coloring="lines")), ) - assert len(p.series) == 2 + assert len(p.backend.series) == 2 f = p.fig assert len(f.data) == 2 assert isinstance(f.data[0], go.Contour) @@ -439,82 +442,99 @@ def test_plot_vector_2d_quivers(): assert f.data[0]["contours"]["coloring"] == "lines" assert f.data[0]["colorbar"]["title"]["text"] == "Magnitude" assert f.data[1]["line"]["color"] == "red" - - -def test_plot_vector_2d_streamlines_custom_scalar_field(): + p.backend.update_interactive({a: 2}) + + +@pytest.mark.parametrize( + "scalar, use_latex, expected_label", [ + (True, False, "Magnitude"), + (True, True, "Magnitude"), + (x + y, False, "x + y"), + (x + y, True, "$x + y$"), + ([(x + y), "test"], False, "test"), + ([(x + y), "test"], True, "test") + ] +) +def test_plot_vector_2d_streamlines_custom_scalar_field(scalar, use_latex, expected_label): # verify that the backends produce the expected results when # `plot_vector()` is called and `contour_kw`/`stream_kw` overrides the # default settings - p = make_plot_vector_2d_streamlines_1( + p = make_test_plot_vector_2d_streamlines( PB, stream_kw=dict(line_color="red"), contour_kw=dict(contours=dict(coloring="lines")), + scalar=scalar, use_latex=use_latex ) - assert len(p.series) == 2 + assert len(p.backend.series) == 2 f = p.fig assert len(f.data) == 2 assert isinstance(f.data[0], go.Contour) assert isinstance(f.data[1], go.Scatter) assert f.data[0]["contours"]["coloring"] == "lines" - assert f.data[0]["colorbar"]["title"]["text"] == "x + y" + assert f.data[0]["colorbar"]["title"]["text"] == expected_label assert f.data[1]["line"]["color"] == "red" + raises(NotImplementedError, lambda :p.backend.update_interactive({a: 2})) -def test_plot_vector_2d_streamlines_custom_scalar_field_custom_label(): - # verify that the backends produce the expected results when - # `plot_vector()` is called and `contour_kw`/`stream_kw` overrides the - # default settings - - p = make_plot_vector_2d_streamlines_2( - PB, - stream_kw=dict(line_color="red"), - contour_kw=dict(contours=dict(coloring="lines")), - ) - f = p.fig - assert f.data[0]["colorbar"]["title"]["text"] == "test" - - -def test_plot_vector_3d_quivers(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_vector_3d_quivers(use_latex, label_func): # verify that the backends produce the expected results when # `plot_vector()` is called and `quiver_kw` overrides the # default settings - p = make_plot_vector_3d_quiver(PB, quiver_kw=dict(sizeref=5)) - assert len(p.series) == 1 + p = make_test_plot_vector_3d_quiver_streamlines( + PB, False, quiver_kw=dict(sizeref=5), + use_latex=use_latex) + assert len(p.backend.series) == 1 f = p.fig assert len(f.data) == 1 assert isinstance(f.data[0], go.Cone) assert f.data[0]["sizeref"] == 5 - assert f.data[0]["colorbar"]["title"]["text"] == str((z, y, x)) - + assert f.data[0]["colorbar"]["title"]["text"] == label_func( + use_latex, (a * z, y, x)) cs1 = f.data[0]["colorscale"] + p.backend.update_interactive({a: 2}) - p = make_plot_vector_3d_quiver(PB, quiver_kw=dict(colorscale="reds")) + p = make_test_plot_vector_3d_quiver_streamlines( + PB, False, quiver_kw=dict(colorscale="reds"), + use_latex=use_latex) f = p.fig cs2 = f.data[0]["colorscale"] assert len(cs1) != len(cs2) -def test_plot_vector_3d_streamlines(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_vector_3d_streamlines(use_latex, label_func): # verify that the backends produce the expected results when # `plot_vector()` is called and `stream_kw` overrides the # default settings - p = make_plot_vector_3d_streamlines_1( - PB, stream_kw=dict(colorscale=[[0, "red"], [1, "red"]]) + p = make_test_plot_vector_3d_quiver_streamlines( + PB, True, stream_kw=dict(colorscale=[[0, "red"], [1, "red"]]), + use_latex=use_latex ) - assert len(p.series) == 1 + assert len(p.backend.series) == 1 f = p.fig assert len(f.data) == 1 assert isinstance(f.data[0], go.Streamtube) assert f.data[0]["colorscale"] == ((0, "red"), (1, "red")) - assert f.data[0]["colorbar"]["title"]["text"] == str((z, y, x)) + assert f.data[0]["colorbar"]["title"]["text"] == label_func( + use_latex, (a*z, y, x)) + raises( + NotImplementedError, + lambda: p.backend.update_interactive({a: 2}) + ) # test different combinations for streamlines: it should not raise errors - p = make_plot_vector_3d_streamlines_1(PB, stream_kw=dict(starts=True)) - p = make_plot_vector_3d_streamlines_1( - PB, + p = make_test_plot_vector_3d_quiver_streamlines(PB, True, + stream_kw=dict(starts=True)) + p = make_test_plot_vector_3d_quiver_streamlines( + PB, True, stream_kw=dict( starts={ "x": np.linspace(-5, 5, 10), @@ -525,8 +545,8 @@ def test_plot_vector_3d_streamlines(): ) # other keywords: it should not raise errors - p = make_plot_vector_3d_streamlines_1( - PB, stream_kw=dict(), kwargs=dict(use_cm=False) + p = make_test_plot_vector_3d_quiver_streamlines( + PB, True, stream_kw=dict(), kwargs=dict(use_cm=False) ) @@ -536,8 +556,8 @@ def test_plot_vector_2d_normalize(): # that data in the figures is different in the two cases normalize=True # and normalize=False - p1 = make_plot_vector_2d_normalize_1(PB, False) - p2 = make_plot_vector_2d_normalize_1(PB, True) + p1 = make_test_plot_vector_2d_normalize(PB, False) + p2 = make_test_plot_vector_2d_normalize(PB, True) d1x = np.array(p1.fig.data[0].x).astype(float) d1y = np.array(p1.fig.data[0].y).astype(float) d2x = np.array(p2.fig.data[0].x).astype(float) @@ -545,9 +565,9 @@ def test_plot_vector_2d_normalize(): assert not np.allclose(d1x, d2x, equal_nan=True) assert not np.allclose(d1y, d2y, equal_nan=True) - p1 = make_plot_vector_2d_normalize_2(PB, False) + p1 = make_test_plot_vector_2d_normalize(PB, False) p1.backend.update_interactive({u: 1.5}) - p2 = make_plot_vector_2d_normalize_2(PB, True) + p2 = make_test_plot_vector_2d_normalize(PB, True) p2.backend.update_interactive({u: 1.5}) d1x = np.array(p1.fig.data[0].x).astype(float) d1y = np.array(p1.fig.data[0].y).astype(float) @@ -564,15 +584,15 @@ def test_plot_vector_3d_normalize(): # that data in the figures is different in the two cases normalize=True # and normalize=False - p1 = make_plot_vector_3d_normalize_1(PB, False) - p2 = make_plot_vector_3d_normalize_1(PB, True) + p1 = make_test_plot_vector_3d_normalize(PB, False) + p2 = make_test_plot_vector_3d_normalize(PB, True) assert not np.allclose(p1.fig.data[0]["u"], p2.fig.data[0]["u"]) assert not np.allclose(p1.fig.data[0]["v"], p2.fig.data[0]["v"]) assert not np.allclose(p1.fig.data[0]["w"], p2.fig.data[0]["w"]) - p1 = make_plot_vector_3d_normalize_2(PB, False) + p1 = make_test_plot_vector_3d_normalize(PB, False) p1.backend.update_interactive({u: 1.5}) - p2 = make_plot_vector_3d_normalize_2(PB, True) + p2 = make_test_plot_vector_3d_normalize(PB, True) p2.backend.update_interactive({u: 1.5}) assert not np.allclose(p1.fig.data[0]["u"], p2.fig.data[0]["u"]) assert not np.allclose(p1.fig.data[0]["v"], p2.fig.data[0]["v"]) @@ -604,12 +624,16 @@ def test_plot_implicit_adaptive_false(): ) -def test_plot_real_imag(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_real_imag(use_latex, label_func): # verify that the backends produce the expected results when # `plot_real_imag()` is called and `rendering_kw` overrides the default # settings - p = make_test_real_imag(PB, rendering_kw=dict(line_color="red")) + p = make_test_real_imag(PB, rendering_kw=dict(line_color="red"), + use_latex=use_latex) assert len(p.series) == 2 f = p.fig assert len(f.data) == 2 @@ -620,14 +644,20 @@ def test_plot_real_imag(): assert f.data[1]["name"] == "Im(sqrt(x))" assert f.data[1]["line"]["color"] == "red" assert f.layout["showlegend"] is True + assert f.layout["xaxis"]["title"]["text"] == label_func(use_latex, x) + assert f.layout["yaxis"]["title"]["text"] == r"$f\left(x\right)$" if use_latex else "f(x)" -def test_plot_complex_1d(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_complex_1d(use_latex): # verify that the backends produce the expected results when # `plot_complex()` is called and `rendering_kw` overrides the default # settings - p = make_test_plot_complex_1d(PB, rendering_kw=dict(line_color="red")) + p = make_test_plot_complex_1d(PB, rendering_kw=dict(line_color="red"), + use_latex=use_latex) assert len(p.series) == 1 f = p.fig assert len(f.data) == 1 @@ -635,21 +665,30 @@ def test_plot_complex_1d(): assert f.data[0]["name"] == "Arg(sqrt(x))" assert f.data[0]["line"]["color"] == "red" assert p.fig.data[0]["marker"]["colorbar"]["title"]["text"] == "Arg(sqrt(x))" + assert f.layout["xaxis"]["title"]["text"] == "Real" + assert f.layout["yaxis"]["title"]["text"] == "Abs" -def test_plot_complex_2d(): +@pytest.mark.parametrize( + "use_latex", [True, False] +) +def test_plot_complex_2d(use_latex, label_func): # verify that the backends produce the expected results when # `plot_complex()` is called and `rendering_kw` overrides the default # settings - p = make_test_plot_complex_2d(PB, rendering_kw=dict()) + p = make_test_plot_complex_2d(PB, rendering_kw=dict(), + use_latex=use_latex) assert len(p.series) == 1 f = p.fig assert len(f.data) == 2 assert isinstance(f.data[0], go.Image) - assert f.data[0]["name"] == "sqrt(x)" + # TODO: there must be some bugs here with the wrapper $$ + assert f.data[0]["name"] == "$sqrt(x)$" if use_latex else "sqrt(x)" assert isinstance(f.data[1], go.Scatter) assert f.data[1]["marker"]["colorbar"]["title"]["text"] == "Argument" + assert f.layout["xaxis"]["title"]["text"] == "Re" + assert f.layout["yaxis"]["title"]["text"] == "Im" def test_plot_complex_3d(): @@ -667,24 +706,20 @@ def test_plot_complex_3d(): assert f.data[0]["colorbar"]["title"]["text"] == "Argument" -def test_plot_list_is_filled_false(): +@pytest.mark.parametrize( + "is_filled", [True, False] +) +def test_plot_list_is_filled(is_filled): # verify that the backends produce the expected results when # `plot_list()` is called with `is_filled=False` - p = make_test_plot_list_is_filled_false(PB) + p = make_test_plot_list_is_filled(PB, is_filled) assert len(p.series) == 1 f = p.fig - assert f.data[0]["marker"]["line"]["color"] is not None - - -def test_plot_list_is_filled_true(): - # verify that the backends produce the expected results when - # `plot_list()` is called with `is_filled=True` - - p = make_test_plot_list_is_filled_true(PB) - assert len(p.series) == 1 - f = p.fig - assert f.data[0]["marker"]["line"]["color"] is None + if not is_filled: + assert f.data[0]["marker"]["line"]["color"] is not None + else: + assert f.data[0]["marker"]["line"]["color"] is None def test_plot_list_color_func(): @@ -774,17 +809,6 @@ def test_save(): p.save(os.path.join(tmpdir, filename), include_plotlyjs="cdn") -def test_vectors_3d_update_interactive(): - # Some backends do not support streamlines with iplot. Test that the - # backends raise error. - - p = make_test_vectors_3d_update_interactive(PB) - raises( - NotImplementedError, - lambda: p.backend.update_interactive({a: 2, b: 2, c: 2}) - ) - - def test_aspect_ratio_2d_issue_11764(): # verify that the backends apply the provided aspect ratio. # NOTE: read the backend docs to understand which options are available. @@ -798,16 +822,18 @@ def test_aspect_ratio_2d_issue_11764(): assert p.fig.layout.yaxis.scaleanchor == "x" -def test_aspect_ratio_3d(): +@pytest.mark.parametrize( + "aspect, expected", [ + ("auto", "auto"), + ("cube", "cube"), + ] +) +def test_aspect_ratio_3d(aspect, expected): # verify that the backends apply the provided aspect ratio. - p = make_test_aspect_ratio_3d(PB) - assert p.aspect == "auto" - assert p.fig.layout.scene.aspectmode == "auto" - - p = make_test_aspect_ratio_3d(PB, "cube") - assert p.aspect == "cube" - assert p.fig.layout.scene.aspectmode == "cube" + p = make_test_aspect_ratio_3d(PB, aspect) + assert p.aspect == aspect + assert p.fig.layout.scene.aspectmode == expected def test_plot_size(): @@ -824,21 +850,21 @@ def test_plot_size(): @pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_scale_lin_log(): +@pytest.mark.parametrize( + "xscale, yscale", [ + ("linear", "linear"), + ("log", "linear"), + ("linear", "log"), + ("log", "log"), + ] +) +def test_plot_scale_lin_log(xscale, yscale): # verify that backends are applying the correct scale to the axes # NOTE: none of the 3D libraries currently support log scale. - p = make_test_plot_scale_lin_log(PB, "linear", "linear") - assert p.fig.layout["xaxis"]["type"] == "linear" - assert p.fig.layout["yaxis"]["type"] == "linear" - - p = make_test_plot_scale_lin_log(PB, "log", "linear") - assert p.fig.layout["xaxis"]["type"] == "log" - assert p.fig.layout["yaxis"]["type"] == "linear" - - p = make_test_plot_scale_lin_log(PB, "linear", "log") - assert p.fig.layout["xaxis"]["type"] == "linear" - assert p.fig.layout["yaxis"]["type"] == "log" + p = make_test_plot_scale_lin_log(PB, xscale, yscale) + assert p.fig.layout["xaxis"]["type"] == xscale + assert p.fig.layout["yaxis"]["type"] == yscale def test_backend_latex_labels(): @@ -868,152 +894,6 @@ def test_backend_latex_labels(): assert p2.zlabel == p2.fig.layout.scene.zaxis.title.text == "f(x_1^2, x_2)" -def test_plot_use_latex(): - # verify that the backends produce the expected results when `plot()` - # is called and `rendering_kw` overrides the default line settings - - p = make_test_plot_use_latex(PB) - f = p.fig - assert f.data[0]["name"] == "$\\sin{\\left(x \\right)}$" - assert f.data[1]["name"] == "$\\cos{\\left(x \\right)}$" - assert f.layout["showlegend"] is True - - -def test_plot_parametric_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_parametric_use_latex(PB) - f = p.fig - assert f.data[0]["name"] == "$x$" - assert f.data[0]["marker"]["colorbar"]["title"]["text"] == "$x$" - - -def test_plot_contour_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_contour_use_latex(PB) - f = p.fig - assert f.data[0]["colorbar"]["title"]["text"] == "$%s$" % latex( - cos(x**2 + y**2) - ) - - -def test_plot3d_parametric_line_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot3d_parametric_line_use_latex(PB) - f = p.fig - assert f.data[0]["name"] == "$x$" - assert f.data[0]["line"]["colorbar"]["title"]["text"] == "$x$" - - -def test_plot3d_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot3d_use_latex(PB) - f = p.fig - assert f.data[0].colorbar.title.text == "$%s$" % latex(cos(x**2 + y**2)) - assert f.data[1].colorbar.title.text == "$%s$" % latex(sin(x**2 + y**2)) - assert f.data[0].name == "$%s$" % latex(cos(x**2 + y**2)) - assert f.data[1].name == "$%s$" % latex(sin(x**2 + y**2)) - assert f.data[0]["showscale"] - assert f.layout["showlegend"] is False - - -def test_plot_vector_2d_quivers_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_quivers_use_latex(PB) - f = p.fig - assert f.data[0]["colorbar"]["title"]["text"] == "Magnitude" - - -def test_plot_vector_2d_streamlines_custom_scalar_field_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_streamlines_custom_scalar_field_use_latex(PB) - f = p.fig - assert f.data[0]["colorbar"]["title"]["text"] == "$x + y$" - - -def test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_streamlines_custom_scalar_field_custom_label_use_latex( - PB - ) - f = p.fig - assert f.data[0]["colorbar"]["title"]["text"] == "test" - - -def test_plot_vector_2d_use_latex_colorbar(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_2d_use_latex_colorbar(PB, True, False) - assert p.fig.data[0]["colorbar"]["title"]["text"] == "Magnitude" - - p = make_test_plot_vector_2d_use_latex_colorbar(PB, True, True) - assert p.fig.data[0]["colorbar"]["title"]["text"] == "Magnitude" - - p = make_test_plot_vector_2d_use_latex_colorbar(PB, False, False) - assert p.fig.data[0]["name"] == "$\\left( x, \\ y\\right)$" - - p = make_test_plot_vector_2d_use_latex_colorbar(PB, False, True) - assert p.fig.data[0]["name"] == "$\\left( x, \\ y\\right)$" - - -def test_plot_vector_3d_quivers_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_3d_quivers_use_latex(PB) - assert ( - p.fig.data[0]["colorbar"]["title"]["text"] - == "$\\left( z, \\ y, \\ x\\right)$" - ) - - -def test_plot_vector_3d_streamlines_use_latex(): - # verify that the colorbar uses latex label - - p = make_test_plot_vector_3d_streamlines_use_latex(PB) - assert ( - p.fig.data[0]["colorbar"]["title"]["text"] - == "$\\left( z, \\ y, \\ x\\right)$" - ) - - -@pytest.mark.filterwarnings("ignore::RuntimeWarning") -def test_plot_complex_use_latex(): - # complex plot function should return the same result (for axis labels) - # wheter use_latex is True or False - - p = make_test_plot_complex_use_latex_1(PB) - assert p.fig.layout.xaxis.title.text == "Real" - assert p.fig.layout.yaxis.title.text == "Abs" - assert p.fig.data[0].name == "Arg(cos(x) + I*sinh(x))" - assert ( - p.fig.data[0]["marker"]["colorbar"]["title"]["text"] - == "Arg(cos(x) + I*sinh(x))" - ) - - p = make_test_plot_complex_use_latex_2(PB) - assert p.fig.layout.xaxis.title.text == "Re" - assert p.fig.layout.yaxis.title.text == "Im" - assert p.fig.data[0].name == "$gamma(z)$" - assert p.fig.data[1]["marker"]["colorbar"]["title"]["text"] == "Argument" - - -def test_plot_real_imag_use_latex(): - # real/imag plot function should return the same result (for axis labels) - # wheter use_latex is True or False - - p = make_test_plot_real_imag_use_latex(PB) - assert p.fig.layout.xaxis.title.text == "$x$" - assert p.fig.layout.yaxis.title.text == r"$f\left(x\right)$" - assert p.fig.data[0]["name"] == "Re(sqrt(x))" - assert p.fig.data[1]["name"] == "Im(sqrt(x))" - - def test_plot3d_use_cm(): # verify that use_cm produces the expected results on plot3d @@ -1064,59 +944,23 @@ def test_surface_color_func(): # Verify that backends do not raise errors when plotting surfaces and that # the color function is applied. - p1 = make_test_surface_color_func_1(PB, lambda x, y, z: z) - p2 = make_test_surface_color_func_1( - PB, lambda x, y, z: np.sqrt(x**2 + y**2)) - assert not np.allclose( - p1.fig.data[0]["surfacecolor"], p2.fig.data[0]["surfacecolor"] - ) - - p1 = make_test_surface_color_func_2(PB, lambda x, y, z, u, v: z) - p2 = make_test_surface_color_func_2( - PB, lambda x, y, z, u, v: np.sqrt(x**2 + y**2) - ) - assert not np.allclose( - p1.fig.data[0]["surfacecolor"], p2.fig.data[0]["surfacecolor"] - ) - - -def test_surface_interactive_color_func(): - # After the addition of `color_func`, `SurfaceInteractiveSeries` and - # `ParametricSurfaceInteractiveSeries` returns different elements. - # Verify that backends do not raise errors when updating surfaces and a - # color function is applied. - - p = make_test_surface_interactive_color_func(PB) - p.update_interactive({t: 2}) + p = make_test_surface_color_func(PB) assert not np.allclose( - p.fig.data[0]["surfacecolor"], - p.fig.data[1]["surfacecolor"] + p.fig.data[0]["surfacecolor"], p.fig.data[1]["surfacecolor"] ) assert not np.allclose( - p.fig.data[2]["surfacecolor"], - p.fig.data[3]["surfacecolor"] + p.fig.data[2]["surfacecolor"], p.fig.data[3]["surfacecolor"] ) + p.backend.update_interactive({t: 2}) def test_line_color_func(): # Verify that backends do not raise errors when plotting lines and that # the color function is applied. - p1 = make_test_line_color_func(PB, None) - p2 = make_test_line_color_func(PB, lambda x, y: np.cos(x)) - assert p1.fig.data[0].marker.color is None - assert np.allclose( - p2.fig.data[0].marker.color, - np.cos(np.linspace(-3, 3, 5)) - ) - - -def test_line_interactive_color_func(): - # Verify that backends do not raise errors when updating lines and a - # color function is applied. - - p = make_test_line_interactive_color_func(PB) - p.update_interactive({t: 2}) + p = make_test_line_color_func(PB) + assert p.fig.data[0].marker.color is None + p.backend.update_interactive({t: 2}) assert p.fig.data[0].marker.color is None assert np.allclose( p.fig.data[1].marker.color, @@ -1186,180 +1030,12 @@ def test_update_interactive(): u, v, x, y, z = symbols("u, v, x:z") - def do_test(p, params, prop): - p.backend.draw() - d1 = p.backend[0].get_data() - v1 = np.array(p.fig.data[0][prop]) - # quivers contain None, which is not comparable with np.allclose - v1[v1 == None] = np.nan - p.backend.update_interactive(params) - d2 = p.backend[0].get_data() - v2 = np.array(p.fig.data[0][prop]) - v2[v2 == None] = np.nan - c1 = not all(np.allclose(s, t) for s, t in zip(d1, d2)) - c2 = not np.allclose( - v1.astype(float), v2.astype(float), equal_nan=True - ) - return c1 and c2 - - p = plot( - sin(u * x), - (x, -pi, pi), - adaptive=False, - n=5, - backend=PB, - show=False, - params={u: (1, 0, 2)}, - ) - assert do_test(p, {u: 2}, "y") - - p = plot_polar( - 1 + sin(10 * u * x) / 10, - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=PB, - show=False, - params={u: (1, 0, 2)}, - ) - assert do_test(p, {u: 1.5}, "y") - - p = plot_parametric( - cos(u * x), - sin(u * x), - (x, 0, 2 * pi), - adaptive=False, - n=5, - backend=PB, - show=False, - params={u: (1, 0, 2)}, - ) - assert do_test(p, {u: 2}, "y") - - # points - p = plot3d_parametric_line( - cos(u * x), - sin(x), - x, - (x, -pi, pi), - backend=PB, - is_point=True, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - ) - assert do_test(p, {u: 2}, "x") - - # line - p = plot3d_parametric_line( - cos(u * x), - sin(x), - x, - (x, -pi, pi), - backend=PB, - is_point=False, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - use_cm=False, - ) - assert do_test(p, {u: 2}, "x") - - p = plot3d_parametric_line( - cos(u * x), - sin(x), - x, - (x, -pi, pi), - backend=PB, - is_point=False, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - use_cm=True, - ) - assert do_test(p, {u: 2}, "x") - - p = plot3d( - cos(u * x**2 + y**2), - (x, -2, 2), - (y, -2, 2), - backend=PB, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - ) - assert do_test(p, {u: 2}, "z") - - p = plot_contour( - cos(u * x**2 + y**2), - (x, -2, 2), - (y, -2, 2), - backend=PB, - show=False, - adaptive=False, - n=5, - params={u: (1, 0, 2)}, - ) - assert do_test(p, {u: 2}, "z") - - u, v = symbols("u, v") - fx = (1 + v / 2 * cos(u / 2)) * cos(x * u) - fy = (1 + v / 2 * cos(u / 2)) * sin(x * u) - fz = v / 2 * sin(u / 2) - p = plot3d_parametric_surface( - fx, - fy, - fz, - (u, 0, 2 * pi), - (v, -1, 1), - backend=PB, - use_cm=True, - n1=5, - n2=5, - show=False, - params={x: (1, 0, 2)}, - ) - assert do_test(p, {x: 2}, "x") - - p = plot_vector( - Matrix([-u * y, x]), - (x, -5, 5), - (y, -4, 4), - backend=PB, - n=4, - show=False, - params={u: (1, 0, 2)}, - scalar=False, - ) - assert do_test(p, {u: 2}, "x") - - p = plot_vector( - Matrix([u * z, y, x]), - (x, -5, 5), - (y, -4, 4), - (z, -3, 3), - backend=PB, - n=4, - show=False, - params={u: (1, 0, 2)}, - ) - assert do_test(p, {u: 2}, "u") - p = plot_complex( - sqrt(u * x), - (x, -5 - 5 * I, 5 + 5 * I), - show=False, - backend=PB, - threed=True, - use_cm=True, - n=5, + sqrt(u * x), (x, -5 - 5 * I, 5 + 5 * I), + show=False, backend=PB, threed=True, use_cm=True, n=5, params={u: (1, 0, 2)}, ) - assert do_test(p, {u: 2}, "z") + p.backend.update_interactive({u: 2}) from sympy.geometry import Line as SymPyLine @@ -1367,25 +1043,19 @@ def do_test(p, params, prop): SymPyLine((u, 2), (5, 4)), Circle((0, 0), u), Polygon((2, u), 3, n=6), - backend=PB, - show=False, - is_filled=False, - use_latex=False, + backend=PB, show=False, is_filled=False, use_latex=False, params={u: (1, 0, 2)}, ) - assert do_test(p, {u: 2}, "x") + p.backend.update_interactive({u: 2}) p = plot_geometry( SymPyLine((u, 2), (5, 4)), Circle((0, 0), u), Polygon((2, u), 3, n=6), - backend=PB, - show=False, - is_filled=True, - use_latex=False, + backend=PB, show=False, is_filled=True, use_latex=False, params={u: (1, 0, 2)}, ) - assert do_test(p, {u: 2}, "x") + p.backend.update_interactive({u: 2}) def test_generic_data_series(): @@ -1458,70 +1128,67 @@ def test_scatter_gl(): assert isinstance(f4.data[0], go.Scatterpolargl) -def test_plot3d_list_use_cm_False(): +def test_plot3d_list(): # verify that plot3d_list produces the expected results when no color map # is required - # solid color line - p = make_test_plot3d_list_use_cm_False(PB, False, False) - assert p.fig.data[0].mode == "lines" - assert p.fig.data[0].line.color == "#636EFA" - - # solid color markers with empty faces - p = make_test_plot3d_list_use_cm_False(PB, True, False) - assert p.fig.data[0].mode == "markers" - assert p.fig.data[0].marker.color == "#E5ECF6" - assert p.fig.data[0].marker.line.color == "#636EFA" - - # solid color markers with filled faces - p = make_test_plot3d_list_use_cm_False(PB, True, True) - assert p.fig.data[0].marker.color == "#636EFA" - - -def test_plot3d_list_use_cm_color_func(): - # verify that use_cm=True and color_func do their job - - # line with colormap - # if color_func is not provided, the same parameter will be used - # for all points - p1 = make_test_plot3d_list_use_cm_color_func(PB, False, False, None) - c1 = p1.fig.data[0].line.color - p2 = make_test_plot3d_list_use_cm_color_func( - PB, False, False, lambda x, y, z: x - ) - c2 = p2.fig.data[0].line.color - assert not np.allclose(c1, c2) - - # markers with empty faces - p1 = make_test_plot3d_list_use_cm_color_func(PB, False, False, None) - c1 = p1.fig.data[0].line.color - p2 = make_test_plot3d_list_use_cm_color_func( - PB, False, False, lambda x, y, z: x - ) - c2 = p2.fig.data[0].line.color - assert not np.allclose(c1, c2) + # TODO: these tests dont't make any sense, colors are different from what + # I see on Jupyter Notebook. WTF??? + p = make_test_plot3d_list(PB, False, None) + fig = p.fig + assert fig.data[2].mode == "lines" + assert fig.data[2].line.color == "#00CC96" + assert fig.data[1].mode == "markers" + assert fig.data[1].marker.color == "#E5ECF6" + assert fig.data[1].marker.line.color == "#EF553B" + assert fig.data[0].mode == "markers" + assert fig.data[0].marker.color == "#E5ECF6" + assert np.allclose(fig.data[0].marker.line.color, 1) + # assert np.allclose(fig.data[0].line.color, 0) + p.backend.update_interactive({t: 1}) + p = make_test_plot3d_list(PB, True, None) + fig = p.fig + assert fig.data[2].mode == "lines" + assert fig.data[2].line.color == "#00CC96" + assert fig.data[1].mode == "markers" + assert fig.data[1].marker.color == "#EF553B" + assert fig.data[1].marker.line.color is None + assert fig.data[0].mode == "markers" + assert np.allclose(fig.data[0].marker.color, 1) + assert fig.data[0].line.color is None + p.backend.update_interactive({t: 1}) -def test_plot3d_list_interactive(): - # verify that no errors are raises while updating a plot3d_list + p = make_test_plot3d_list(PB, False, lambda x, y, z: z) + fig = p.fig + assert fig.data[2].mode == "lines" + assert fig.data[2].line.color == "#00CC96" + assert fig.data[1].mode == "markers" + assert fig.data[1].marker.color == "#E5ECF6" + assert fig.data[1].marker.line.color == "#EF553B" + assert fig.data[0].mode == "markers" + assert not np.allclose(fig.data[0].marker.line.color, 1) + p.backend.update_interactive({t: 1}) - p = make_test_plot3d_list_interactive(PB) + p = make_test_plot3d_list(PB, True, lambda x, y, z: z) + p.fig + assert fig.data[2].mode == "lines" + assert fig.data[2].line.color == "#00CC96" + assert fig.data[1].mode == "markers" + assert fig.data[1].marker.color == "#E5ECF6" + assert fig.data[1].marker.line.color == "#EF553B" + assert fig.data[0].mode == "markers" + assert not np.allclose(fig.data[0].marker.line.color, 1) p.backend.update_interactive({t: 1}) def test_contour_show_clabels(): - p = make_test_contour_show_clabels_1(PB, False) - assert not p.fig.data[0].contours.showlabels - - p = make_test_contour_show_clabels_1(PB, True) - assert p.fig.data[0].contours.showlabels - - p = make_test_contour_show_clabels_2(PB, False) - p.backend.update_interactive({Symbol("u"): 2}) + p = make_test_contour_show_clabels(PB, False) + p.backend.update_interactive({a: 2}) assert not p.fig.data[0].contours.showlabels - p = make_test_contour_show_clabels_2(PB, True) - p.backend.update_interactive({Symbol("u"): 2}) + p = make_test_contour_show_clabels(PB, True) + p.backend.update_interactive({a: 2}) assert p.fig.data[0].contours.showlabels @@ -1869,9 +1536,9 @@ def test_arrow_2d(): p = make_test_arrow_2d(PB, "test", {"arrowcolor": "red"}, True) fig = p.fig assert len(fig.layout.annotations) == 1 - assert fig.layout.annotations[0]["text"] == "test" + assert fig.layout.annotations[0]["text"] == "$test$" assert fig.layout.annotations[0]["arrowcolor"] == "red" - p._backend.update_interactive({a: 4, b: 5}) + p.backend.update_interactive({a: 4, b: 5}) p = make_test_arrow_2d(PB, "test", {"arrowcolor": "red"}, False) fig = p.fig @@ -1953,7 +1620,7 @@ def test_plotly_update_ranges(update_event, fig_type): def test_hvlines(): a, b = symbols("a, b") p = make_test_hvlines(PB) - fig = p._backend.fig + fig = p.backend.fig assert len(fig.layout.shapes) == 2 l1, l2 = fig.layout.shapes assert l1["xref"] == "x domain" @@ -1962,4 +1629,4 @@ def test_hvlines(): [l1.x0, l1.x1, l1.y0, l1.y1], [l2.x0, l2.x1, l2.y0, l2.y1] ) - p._backend.update_interactive({a: 3, b: 4}) + p.backend.update_interactive({a: 3, b: 4}) diff --git a/tests/conftest.py b/tests/conftest.py index 7601d003..630a1e33 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,6 +1,16 @@ import pytest from spb.defaults import cfg from spb import MB +from sympy import latex + + +@pytest.fixture +def label_func(): + def wrapper(use_latex, expr): + if use_latex: + return "$%s$" % latex(expr) + return str(expr) + return wrapper @pytest.fixture