Example #1
0
    def test_add_plugin_module_import_failure(self):
        """Test a module import failure when adding a plugin."""
        enc = Encoder(RLELossless)

        msg = r"No module named 'badpath'"
        with pytest.raises(ModuleNotFoundError, match=msg):
            enc.add_plugin("foo", ('badpath', '_encode_frame'))
        assert {} == enc._available
        assert {} == enc._unavailable
Example #2
0
 def test_add_plugin_unavailable(self):
     """Test adding an unavailable plugin."""
     enc = Encoder(RLELossless)
     assert not enc.is_available
     enc.add_plugin("foo",
                    ('pydicom.encoders.pylibjpeg', 'encode_pixel_data'))
     assert enc._available == {}
     assert "foo" in enc._unavailable
     assert enc._unavailable["foo"] == ("numpy", "pylibjpeg",
                                        "pylibjpeg-rle")
     assert not enc.is_available
Example #3
0
    def test_add_plugin_function_missing(self):
        """Test encoding function missing when adding a plugin."""
        enc = Encoder(RLELossless)

        msg = (r"module 'pydicom.encoders.native' has no "
               r"attribute 'bad_function_name'")
        with pytest.raises(AttributeError, match=msg):
            enc.add_plugin(
                "foo",
                ('pydicom.encoders.native', 'bad_function_name'),
            )
        assert {} == enc._available
        assert {} == enc._unavailable
Example #4
0
 def test_add_plugin_unavailable(self):
     """Test adding an unavailable plugin."""
     enc = Encoder(RLELossless)
     assert not enc.is_available
     enc.add_plugin(
         "foo",
         ('pydicom.pixel_data_handlers.rle_handler',
          '_wrap_rle_encode_frame'),
     )
     assert {} == enc._available
     assert "foo" in enc._unavailable
     assert ("numpy", ) == enc._unavailable["foo"]
     assert not enc.is_available
Example #5
0
 def test_init(self):
     """Test creating a new Encoder"""
     uid = UID('1.2.3')
     enc = Encoder(uid)
     assert {} == enc._available
     assert {} == enc._unavailable
     assert '<' == enc._defaults['byteorder']
     assert uid == enc._defaults['transfer_syntax_uid']
Example #6
0
    def test_kwargs_from_ds(self):
        """Test Encoder.kwargs_from_ds()"""
        # Note no NumberOfFrames element
        ds = Dataset()
        ds.Rows = 10
        ds.Columns = 12
        ds.SamplesPerPixel = 1
        ds.BitsAllocated = 8
        ds.BitsStored = 8
        ds.PixelRepresentation = 0
        ds.PhotometricInterpretation = 'RGB'

        enc = Encoder(RLELossless)
        kwargs = enc.kwargs_from_ds(ds)
        assert 1 == kwargs['number_of_frames']
        assert enc._check_kwargs(kwargs) is None

        # Test conversion of empty *Number of Frames*
        ds.NumberOfFrames = None
        kwargs = enc.kwargs_from_ds(ds)
        assert 1 == kwargs['number_of_frames']

        # Test already present *Number of Frames* is unaffected
        ds.NumberOfFrames = 10
        kwargs = enc.kwargs_from_ds(ds)
        assert 10 == kwargs['number_of_frames']

        # Test missing elements
        del ds.Columns
        del ds.BitsAllocated

        msg = (
            r"The following required elements are missing from the dataset: "
            r"'Columns', 'BitsAllocated'")
        with pytest.raises(AttributeError, match=msg):
            enc.kwargs_from_ds(ds)

        # Test VM 0
        ds.Columns = None
        ds.BitsAllocated = None

        msg = (r"The following required dataset elements have a VM of 0: "
               r"'Columns', 'BitsAllocated'")
        with pytest.raises(AttributeError, match=msg):
            enc.kwargs_from_ds(ds)
Example #7
0
    def setup(self):
        self.e = Encoder(JPEG2000Lossless)
        self.ds = ds = Dataset()
        ds.Rows = 1
        ds.Columns = 3
        ds.SamplesPerPixel = 1
        ds.PixelRepresentation = 0
        ds.BitsAllocated = 8
        ds.BitsStored = 8
        ds.NumberOfFrames = 1
        ds.PhotometricInterpretation = 'RGB'

        self.arr_3s = np.asarray([
            [[1, 2, 3], [4, 5, 6]],
            [[7, 8, 9], [10, 11, 12]],
            [[13, 14, 15], [16, 17, 18]],
            [[19, 20, 21], [22, 23, 24]],
        ], '|u1')
        assert self.arr_3s.shape == (4, 2, 3)
Example #8
0
    def test_check_kwargs_missing(self):
        """Test _check_kwargs"""
        enc = Encoder(RLELossless)
        kwargs = {
            'rows': 0,
            'columns': 0,
            'samples_per_pixel': 0,
            'bits_allocated': 0,
            'bits_stored': 0,
            'pixel_representation': 0,
            'number_of_frames': 0,
            'photometric_interpretation': 'RGB'
        }
        assert enc._check_kwargs(kwargs) is None

        del kwargs['columns']
        del kwargs['bits_allocated']
        msg = r"Missing expected arguments: 'columns', 'bits_allocated'"
        with pytest.raises(TypeError, match=msg):
            enc._check_kwargs(kwargs)
Example #9
0
 def test_no_plugins(self):
     """Test with no available plugins"""
     enc = Encoder(RLELossless)
     enc.add_plugin('foo',
                    ('pydicom.encoders.pylibjpeg', 'encode_pixel_data'))
     msg = (r"Unable to encode because the encoding plugins are missing "
            r"dependencies:\n    foo - requires numpy, pylibjpeg and "
            r"pylibjpeg-rle")
     with pytest.raises(RuntimeError, match=msg):
         enc._process(b'')
Example #10
0
    def test_remove_plugin_unavailable(self):
        """Test removing a plugin."""
        enc = Encoder(RLELossless)
        enc.add_plugin("foo",
                       ('pydicom.encoders.pylibjpeg', 'encode_pixel_data'))
        assert 'foo' in enc._unavailable
        assert {} == enc._available

        enc.remove_plugin("foo")
        assert {} == enc._unavailable
Example #11
0
    def test_remove_plugin_unavailable(self):
        """Test removing a plugin."""
        enc = Encoder(RLELossless)
        enc.add_plugin(
            "foo",
            ('pydicom.pixel_data_handlers.rle_handler',
             '_wrap_rle_encode_frame'),
        )
        assert 'foo' in enc._unavailable
        assert {} == enc._available

        enc.remove_plugin("foo")
        assert {} == enc._unavailable
Example #12
0
class TestEncoder_Preprocess:
    """Tests for Encoder._preprocess()."""
    def setup(self):
        self.e = Encoder(JPEG2000Lossless)
        self.ds = ds = Dataset()
        ds.Rows = 1
        ds.Columns = 3
        ds.SamplesPerPixel = 1
        ds.PixelRepresentation = 0
        ds.BitsAllocated = 8
        ds.BitsStored = 8
        ds.NumberOfFrames = 1
        ds.PhotometricInterpretation = 'RGB'

        self.arr_3s = np.asarray([
            [[1, 2, 3], [4, 5, 6]],
            [[7, 8, 9], [10, 11, 12]],
            [[13, 14, 15], [16, 17, 18]],
            [[19, 20, 21], [22, 23, 24]],
        ], '|u1')
        assert self.arr_3s.shape == (4, 2, 3)

    def test_invalid_arr_shape_raises(self):
        """Test that an array size and dataset mismatch raise exceptions"""
        # 1D arrays
        arr = np.asarray((1, 2, 3, 4))
        msg = (r"Unable to encode as the shape of the ndarray \(4,\) "
               r"doesn't match the values for the rows, columns and samples "
               r"per pixel")

        kwargs = self.e.kwargs_from_ds(self.ds)
        assert arr.shape == (4, )
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

        # 2D arrays
        arr = np.asarray([[1, 2, 3, 4]])
        assert arr.shape == (1, 4)
        msg = r"Unable to encode as the shape of the ndarray \(1, 4\) "
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

        self.ds.Rows = 2
        self.ds.Columns = 2
        self.ds.SamplesPerPixel = 3
        arr = np.asarray([[1, 2], [3, 4]])
        assert arr.shape == (2, 2)
        msg = r"Unable to encode as the shape of the ndarray \(2, 2\) "
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

        # 3D arrays
        self.ds.Rows = 3
        arr = np.asarray([[[1, 2, 1], [3, 4, 1]]])
        assert arr.shape == (1, 2, 3)
        msg = r"Unable to encode as the shape of the ndarray \(1, 2, 3\) "
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

    def test_invalid_arr_dtype_raises(self):
        """Test an invalid arr dtype raises exception."""
        arr = np.asarray(('a', 'b', 'c'))
        msg = (
            r"Unable to encode as the ndarray's dtype '<U1' is not supported")

        kwargs = self.e.kwargs_from_ds(self.ds)
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

    def test_invalid_pixel_representation_raises(self):
        """Test exception raised if pixel representation/dtype mismatch"""
        self.ds.BitsAllocated = 8
        self.ds.BitsStored = 8
        self.ds.SamplesPerPixel = 1
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 1
        self.ds.Columns = 3
        kwargs = self.e.kwargs_from_ds(self.ds)

        arr = np.asarray([1, 2, 3], dtype='|i1')
        msg = (r"Unable to encode as the ndarray's dtype 'int8' is not "
               r"consistent with pixel representation '0' \(unsigned int\)")
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

        arr = np.asarray([1, 2, 3], dtype='|u1')
        kwargs['pixel_representation'] = 1
        msg = (r"Unable to encode as the ndarray's dtype 'uint8' is not "
               r"consistent with pixel representation '1' \(signed int\)")
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

    def test_invalid_bits_allocated_raises(self):
        """Test exception raised for invalid Bits Allocated"""
        self.ds.BitsAllocated = 8
        self.ds.BitsStored = 8
        self.ds.SamplesPerPixel = 1
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 1
        self.ds.Columns = 3
        kwargs = self.e.kwargs_from_ds(self.ds)

        arr = np.asarray([1, 2, 3], dtype='|u1')
        kwargs['bits_stored'] = 9
        msg = (
            r"Unable to encode as the bits stored value is greater than the "
            r"bits allocated value")
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

        kwargs['bits_stored'] = 8
        kwargs['bits_allocated'] = 9

        msg = (r"Unable to encode as a bits allocated value of 9 is not "
               r"supported \(must be a multiple of 8\)")
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

        kwargs['bits_allocated'] = 16
        msg = (r"Unable to encode as the ndarray's dtype 'uint8' is not "
               r"consistent with a bits allocated value of 16")
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

    def test_invalid_samples_per_pixel_raises(self):
        """Test exception raised spp is invalid"""
        self.ds.BitsAllocated = 8
        self.ds.BitsStored = 8
        self.ds.SamplesPerPixel = 2
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 2
        self.ds.Columns = 2
        kwargs = self.e.kwargs_from_ds(self.ds)

        arr = np.asarray([1, 2, 3], dtype='|i1')
        msg = (r"Unable to encode as a samples per pixel value of 2 is not "
               r"supported \(must be 1 or 3\)")
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

        arr = np.asarray([[1, 2], [1, 3]], dtype='|i1')
        kwargs['samples_per_pixel'] = 3
        msg = (r"Unable to encode as the shape of the ndarray \(2, 2\) is not "
               r"consistent with a samples per pixel value of 3")
        with pytest.raises(ValueError, match=msg):
            self.e._preprocess(arr, **kwargs)

    def test_u08_1s(self):
        """Test processing u8/1s"""
        self.ds.BitsAllocated = 8
        self.ds.BitsStored = 8
        self.ds.SamplesPerPixel = 1
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 1
        self.ds.Columns = 3

        # Override the encoding profile validation
        self.e._uid = None

        arr = np.asarray([1, 2, 3], dtype='|u1')
        assert arr.dtype.itemsize == 1
        kwargs = self.e.kwargs_from_ds(self.ds)
        out = self.e._preprocess(arr, **kwargs)
        assert len(out) == 3
        assert b"\x01\x02\x03" == out

    def test_u08_3s(self):
        """Test processing u8/3s"""
        self.ds.BitsAllocated = 8
        self.ds.BitsStored = 8
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 4
        self.ds.Columns = 2
        self.ds.SamplesPerPixel = 3

        # Override the encoding profile validation
        self.e._uid = None

        arr = self.arr_3s.astype('|u1')
        assert arr.dtype.itemsize == 1
        kwargs = self.e.kwargs_from_ds(self.ds)
        out = self.e._preprocess(arr, **kwargs)
        assert len(out) == 24
        assert out == bytes(range(1, 25))

    def test_i08_1s(self):
        """Test processing i8/1s"""
        self.ds.BitsAllocated = 8
        self.ds.BitsStored = 8
        self.ds.SamplesPerPixel = 1
        self.ds.PixelRepresentation = 1
        self.ds.Rows = 1
        self.ds.Columns = 3

        # Override the encoding profile validation
        self.e._uid = None

        arr = np.asarray([-128, 0, 127], dtype='|i1')
        assert arr.dtype.itemsize == 1
        kwargs = self.e.kwargs_from_ds(self.ds)
        out = self.e._preprocess(arr, **kwargs)
        assert len(out) == 3
        assert out == b"\x80\x00\x7f"

    def test_i08_3s(self):
        """Test processing i8/3s"""
        self.ds.BitsAllocated = 8
        self.ds.BitsStored = 8
        self.ds.PixelRepresentation = 1
        self.ds.Rows = 4
        self.ds.Columns = 2
        self.ds.SamplesPerPixel = 3

        # Override the encoding profile validation
        self.e._uid = None

        arr = self.arr_3s.astype('|i1')
        assert arr.dtype.itemsize == 1
        kwargs = self.e.kwargs_from_ds(self.ds)
        out = self.e._preprocess(arr, **kwargs)
        assert len(out) == 24
        assert out == bytes(range(1, 25))

    def test_u16_1s(self):
        """Test processing u16/1s"""
        self.ds.BitsAllocated = 16
        self.ds.BitsStored = 16
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 1
        self.ds.Columns = 3
        self.ds.SamplesPerPixel = 1

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>u2', '<u2', '=u2'):
            arr = np.asarray([1, 2, 3], dtype=dtype)
            assert arr.dtype.itemsize == 2
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 6
            assert out == b"\x01\x00\x02\x00\x03\x00"

    def test_u16_3s(self):
        """Test processing u16/3s"""
        self.ds.BitsAllocated = 16
        self.ds.BitsStored = 16
        self.ds.SamplesPerPixel = 3
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 4
        self.ds.Columns = 2
        ref = b''.join([bytes([b]) + b'\x00' for b in bytes(range(1, 25))])

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>u2', '<u2', '=u2'):
            arr = self.arr_3s.astype(dtype)
            assert arr.dtype.itemsize == 2
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 48
            assert out == ref

    def test_i16_1s(self):
        """Test processing i16/1s"""
        self.ds.BitsAllocated = 16
        self.ds.BitsStored = 16
        self.ds.PixelRepresentation = 1
        self.ds.Rows = 1
        self.ds.Columns = 3
        self.ds.SamplesPerPixel = 1

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>i2', '<i2', '=i2'):
            arr = np.asarray([-128, 0, 127], dtype=dtype)
            assert arr.dtype.itemsize == 2
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 6
            assert out == b"\x80\xff\x00\x00\x7f\x00"

    def test_i16_3s(self):
        """Test processing i16/3s"""
        self.ds.BitsAllocated = 16
        self.ds.BitsStored = 16
        self.ds.SamplesPerPixel = 3
        self.ds.PixelRepresentation = 1
        self.ds.Rows = 4
        self.ds.Columns = 2
        ref = b''.join([bytes([b]) + b'\x00' for b in bytes(range(1, 25))])

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>i2', '<i2', '=i2'):
            arr = self.arr_3s.astype(dtype)
            assert arr.dtype.itemsize == 2
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 48
            assert out == ref

    def test_u32_1s(self):
        """Test processing u32/1s"""
        self.ds.BitsAllocated = 32
        self.ds.BitsStored = 32
        self.ds.SamplesPerPixel = 1
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 1
        self.ds.Columns = 3
        ref = b"\x01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00"

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>u4', '<u4', '=u4'):
            arr = np.asarray([1, 2, 3], dtype=dtype)
            assert arr.dtype.itemsize == 4
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 12
            assert out == ref

    def test_u32_3s(self):
        """Test processing u32/3s"""
        self.ds.BitsAllocated = 32
        self.ds.BitsStored = 32
        self.ds.SamplesPerPixel = 3
        self.ds.PixelRepresentation = 0
        self.ds.Rows = 4
        self.ds.Columns = 2
        ref = b''.join([bytes([b]) + b'\x00' * 3 for b in bytes(range(1, 25))])

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>u4', '<u4', '=u4'):
            arr = self.arr_3s.astype(dtype)
            assert arr.dtype.itemsize == 4
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 96
            assert out == ref

    def test_i32_1s(self):
        """Test processing i32/1s"""
        self.ds.BitsAllocated = 32
        self.ds.BitsStored = 32
        self.ds.SamplesPerPixel = 1
        self.ds.PixelRepresentation = 1
        self.ds.Rows = 1
        self.ds.Columns = 3
        ref = b"\x80\xff\xff\xff\x00\x00\x00\x00\x7f\x00\x00\x00"

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>i4', '<i4', '=i4'):
            arr = np.asarray([-128, 0, 127], dtype=dtype)
            assert arr.dtype.itemsize == 4
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 12
            assert out == ref

    def test_i32_3s(self):
        """Test processing i32/3s"""
        self.ds.BitsAllocated = 32
        self.ds.BitsStored = 32
        self.ds.SamplesPerPixel = 3
        self.ds.PixelRepresentation = 1
        self.ds.Rows = 4
        self.ds.Columns = 2
        ref = b''.join([bytes([b]) + b'\x00' * 3 for b in bytes(range(1, 25))])

        # Override the encoding profile validation
        self.e._uid = None

        for dtype in ('>i4', '<i4', '=i4'):
            arr = self.arr_3s.astype(dtype)
            assert arr.dtype.itemsize == 4
            kwargs = self.e.kwargs_from_ds(self.ds)
            out = self.e._preprocess(arr, **kwargs)
            assert len(out) == 96
            assert out == ref
Example #13
0
 def test_properties(self):
     """Test Encoder properties"""
     enc = Encoder(RLELossless)
     assert 'RLELosslessEncoder' == enc.name
     assert RLELossless == enc.UID
     assert not enc.is_available
Example #14
0
 def setup(self):
     self.enc = Encoder(UID('1.2.3'))
Example #15
0
class TestEncoder:
    """Non-encoding tests for encoders.Encoder"""
    def setup(self):
        self.enc = Encoder(UID('1.2.3'))

    def test_init(self):
        """Test creating a new Encoder"""
        uid = UID('1.2.3')
        enc = Encoder(uid)
        assert {} == enc._available
        assert {} == enc._unavailable
        assert '<' == enc._defaults['byteorder']
        assert uid == enc._defaults['transfer_syntax_uid']

    def test_properties(self):
        """Test Encoder properties"""
        enc = Encoder(RLELossless)
        assert 'RLELosslessEncoder' == enc.name
        assert RLELossless == enc.UID
        assert not enc.is_available

    @pytest.mark.skipif(not HAVE_NP, reason="Numpy not available")
    def test_add_plugin_available(self):
        """Test adding an available plugin."""
        assert not self.enc.is_available
        self.enc.add_plugin("foo",
                            ('pydicom.encoders.native', '_encode_frame'))
        assert "foo" in self.enc._available
        assert {} == self.enc._unavailable
        assert self.enc.is_available

    @pytest.mark.skipif(HAVE_NP, reason="Numpy is available")
    def test_add_plugin_unavailable(self):
        """Test adding an unavailable plugin."""
        enc = Encoder(RLELossless)
        assert not enc.is_available
        enc.add_plugin("foo",
                       ('pydicom.encoders.pylibjpeg', 'encode_pixel_data'))
        assert enc._available == {}
        assert "foo" in enc._unavailable
        assert enc._unavailable["foo"] == ("numpy", "pylibjpeg",
                                           "pylibjpeg-rle")
        assert not enc.is_available

    def test_add_plugin_module_import_failure(self):
        """Test a module import failure when adding a plugin."""
        enc = Encoder(RLELossless)

        msg = r"No module named 'badpath'"
        with pytest.raises(ModuleNotFoundError, match=msg):
            enc.add_plugin("foo", ('badpath', '_encode_frame'))
        assert {} == enc._available
        assert {} == enc._unavailable

    @pytest.mark.skipif(not HAVE_NP, reason="Numpy is available")
    def test_add_plugin_function_missing(self):
        """Test encoding function missing when adding a plugin."""
        enc = Encoder(RLELossless)

        msg = (r"module 'pydicom.encoders.native' has no "
               r"attribute 'bad_function_name'")
        with pytest.raises(AttributeError, match=msg):
            enc.add_plugin(
                "foo",
                ('pydicom.encoders.native', 'bad_function_name'),
            )
        assert {} == enc._available
        assert {} == enc._unavailable

    @pytest.mark.skipif(not HAVE_NP, reason="Numpy is unavailable")
    def test_add_plugin_twice(self):
        """Test adding a plugin that already exists."""
        self.enc.add_plugin("foo",
                            ('pydicom.encoders.native', '_encode_frame'))
        assert 'foo' in self.enc._available
        assert {} == self.enc._unavailable

        msg = r"'Encoder' already has a plugin named 'foo'"
        with pytest.raises(ValueError, match=msg):
            self.enc.add_plugin("foo",
                                ('pydicom.encoders.native', '_encode_frame'))
        assert 'foo' in self.enc._available
        assert {} == self.enc._unavailable

    @pytest.mark.skipif(not HAVE_NP, reason="Numpy is unavailable")
    def test_remove_plugin(self):
        """Test removing a plugin."""
        self.enc.add_plugin("foo",
                            ('pydicom.encoders.native', '_encode_frame'))
        self.enc.add_plugin("bar",
                            ('pydicom.encoders.native', '_encode_frame'))
        assert 'foo' in self.enc._available
        assert 'bar' in self.enc._available
        assert {} == self.enc._unavailable
        assert self.enc.is_available

        self.enc.remove_plugin("foo")
        assert 'bar' in self.enc._available
        assert self.enc.is_available

        self.enc.remove_plugin("bar")
        assert {} == self.enc._available
        assert not self.enc.is_available

    @pytest.mark.skipif(HAVE_NP, reason="Numpy is available")
    def test_remove_plugin_unavailable(self):
        """Test removing a plugin."""
        enc = Encoder(RLELossless)
        enc.add_plugin("foo",
                       ('pydicom.encoders.pylibjpeg', 'encode_pixel_data'))
        assert 'foo' in enc._unavailable
        assert {} == enc._available

        enc.remove_plugin("foo")
        assert {} == enc._unavailable

    def test_remove_plugin_raises(self):
        """Test removing a plugin that doesn't exist raises exception"""
        msg = r"Unable to remove 'foo', no such plugin"
        with pytest.raises(ValueError, match=msg):
            self.enc.remove_plugin('foo')

    def test_check_kwargs_missing(self):
        """Test _check_kwargs"""
        enc = Encoder(RLELossless)
        kwargs = {
            'rows': 0,
            'columns': 0,
            'samples_per_pixel': 0,
            'bits_allocated': 0,
            'bits_stored': 0,
            'pixel_representation': 0,
            'number_of_frames': 0,
            'photometric_interpretation': 'RGB'
        }
        assert enc._check_kwargs(kwargs) is None

        del kwargs['columns']
        del kwargs['bits_allocated']
        msg = r"Missing expected arguments: 'columns', 'bits_allocated'"
        with pytest.raises(TypeError, match=msg):
            enc._check_kwargs(kwargs)

    def test_kwargs_from_ds(self):
        """Test Encoder.kwargs_from_ds()"""
        # Note no NumberOfFrames element
        ds = Dataset()
        ds.Rows = 10
        ds.Columns = 12
        ds.SamplesPerPixel = 1
        ds.BitsAllocated = 8
        ds.BitsStored = 8
        ds.PixelRepresentation = 0
        ds.PhotometricInterpretation = 'RGB'

        enc = Encoder(RLELossless)
        kwargs = enc.kwargs_from_ds(ds)
        assert 1 == kwargs['number_of_frames']
        assert enc._check_kwargs(kwargs) is None

        # Test conversion of empty *Number of Frames*
        ds.NumberOfFrames = None
        kwargs = enc.kwargs_from_ds(ds)
        assert 1 == kwargs['number_of_frames']

        # Test already present *Number of Frames* is unaffected
        ds.NumberOfFrames = 10
        kwargs = enc.kwargs_from_ds(ds)
        assert 10 == kwargs['number_of_frames']

        # Test missing elements
        del ds.Columns
        del ds.BitsAllocated

        msg = (
            r"The following required elements are missing from the dataset: "
            r"'Columns', 'BitsAllocated'")
        with pytest.raises(AttributeError, match=msg):
            enc.kwargs_from_ds(ds)

        # Test VM 0
        ds.Columns = None
        ds.BitsAllocated = None

        msg = (r"The following required dataset elements have a VM of 0: "
               r"'Columns', 'BitsAllocated'")
        with pytest.raises(AttributeError, match=msg):
            enc.kwargs_from_ds(ds)

    @pytest.mark.skipif(HAVE_NP, reason="Numpy available")
    def test_missing_dependencies(self):
        """Test the required encoder being unavailable."""
        enc = RLELosslessEncoder
        s = enc.missing_dependencies
        assert s[0] == "gdcm - requires gdcm"
        assert (
            s[1] == "pylibjpeg - requires numpy, pylibjpeg and pylibjpeg-rle")

    def test_invalid_profile_raises(self):
        """Test an invalid encoding profile raises exception."""
        ds = get_testdata_file("rtdose_1frame.dcm", read=True)
        assert ds.BitsAllocated == 32  # Invalid for RLE Lossless
        msg = (
            r"Unable to encode as one or more of 'photometric "
            r"interpretation', 'samples per pixel', 'bits allocated', 'bits "
            r"stored' or 'pixel representation' is not valid for 'RLE "
            r"Lossless'")
        with pytest.raises(ValueError, match=msg):
            ds.compress(RLELossless)

    def test_missing_no_dependencies(self):
        """Test an encoder with no dependencies being unavailable."""
        enc = self.enc
        enc._unavailable['foo'] = ()
        s = enc.missing_dependencies
        assert 'foo - plugin indicating it is unavailable' == s[0]

    def test_missing_one_dependency(self):
        """Test an encoder with one dependency being unavailable."""
        enc = self.enc
        enc._unavailable['foo'] = ('bar', )
        s = enc.missing_dependencies
        assert 'foo - requires bar' == s[0]