8000 Baseline image reuse breaks parallel testing · Issue #7673 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content
Baseline image reuse breaks parallel testing #7673
Closed
@QuLogic

Description

@QuLogic

Travis has been failing a lot lately, and I'm pretty sure the reason is baseline image reuse + parallel tests. I believe pytest parallelizes across extension and baseline image, so when a test writes to the same baseline more than once, there's a high possibility that one figure will be saved on top of another while it's trying to verify that it was correct.

For example, the step_linestyle image has been failing a lot recently, which you can try out with:

$ py.test test_axes.py -k linestyle -n 8
...
...
________________________________________________ test_step_linestyle[1-step_linestyle-svg] _________________________________________________
[gw7] linux -- Python 3.5.2 /home/elliott/code/conda/envs/mpl35/bin/python
expected = '/home/elliott/code/matplotlib/lib/matplotlib/tests/result_images/test_axes/step_linestyle-expected_svg.png'
actual = '/home/elliott/code/matplotlib/lib/matplotlib/tests/result_images/test_axes/step_linestyle_svg.png'
tol = 0, in_decorator = True

    def compare_images(expected, actual, tol, in_decorator=False):
        """
        Compare two "image" files checking differences within a tolerance.
    
        The two given filenames may point to files which are convertible to
        PNG via the `.converter` dictionary. The underlying RMS is calculated
        with the `.calculate_rms` function.
    
        Parameters
        ----------
        expected : str
            The filename of the expected image.
        actual :str
            The filename of the actual image.
        tol : float
            The tolerance (a color value difference, where 255 is the
            maximal difference).  The test fails if the average pixel
            difference is greater than this value.
        in_decorator : bool
            If called from image_comparison decorator, this should be
            True. (default=False)
    
        Example
        -------
        img1 = "./baseline/plot.png"
        img2 = "./output/plot.png"
        compare_images( img1, img2, 0.001 ):
    
        """
        if not os.path.exists(actual):
            msg = "Output image %s does not exist." % actual
            raise Exception(msg)
    
        if os.stat(actual).st_size == 0:
            msg = "Output image file %s is empty." % actual
            raise Exception(msg)
    
        verify(actual)
    
        # Convert the image to png
        extension = expected.split('.')[-1]
    
        if not os.path.exists(expected):
            raise IOError('Baseline image %r does not exist.' % expected)
    
        if extension != 'png':
            actual = convert(actual, False)
            expected = convert(expected, True)
    
        # open the image files and remove the alpha channel (if it exists)
        expectedImage = _png.read_png_int(expected)
>       actualImage = _png.read_png_int(actual)
E       SystemError: <built-in function read_png_int> returned NULL without setting an error

which failed reading the png-from-svg, but running again gets:

________________________________________________ test_step_linestyle[0-step_linestyle-svg] _________________________________________________
[gw3] linux -- Python 3.5.2 /home/elliott/code/conda/envs/mpl35/bin/python
expected = '/home/elliott/code/matplotlib/lib/matplotlib/tests/result_images/test_axes/step_linestyle-expected.svg'
actual = '/home/elliott/code/matplotlib/lib/matplotlib/tests/result_images/test_axes/step_linest
61C3
yle.svg'
tol = 0, in_decorator = True

    def compare_images(expected, actual, tol, in_decorator=False):
        """
        Compare two "image" files checking differences within a tolerance.
    
        The two given filenames may point to files which are convertible to
        PNG via the `.converter` dictionary. The underlying RMS is calculated
        with the `.calculate_rms` function.
    
        Parameters
        ----------
        expected : str
            The filename of the expected image.
        actual :str
            The filename of the actual image.
        tol : float
            The tolerance (a color value difference, where 255 is the
            maximal difference).  The test fails if the average pixel
            difference is greater than this value.
        in_decorator : bool
            If called from image_comparison decorator, this should be
            True. (default=False)
    
        Example
        -------
        img1 = "./baseline/plot.png"
        img2 = "./output/plot.png"
        compare_images( img1, img2, 0.001 ):
    
        """
        if not os.path.exists(actual):
            msg = "Output image %s does not exist." % actual
            raise Exception(msg)
    
        if os.stat(actual).st_size == 0:
            msg = "Output image file %s is empty." % actual
>           raise Exception(msg)
E           Exception: Output image file /home/elliott/code/matplotlib/lib/matplotlib/tests/result_images/test_axes/step_linestyle.svg is empty.

which points to a truncated svg.

To fix this, we should be able to stop pytest from parallelizing across baseline images, but what if more than one test uses the same image? I haven't yet checked that case, but it's a possibility. Making some kind of lock-per-baseline-image seems like too much work. Should we start duplicating our baseline images if that's happening?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0