Repository: Rohde-Schwarz/RsWaveform (language: python)

Issue:
npz `Loader` and `Saver`
Loading and saving from numpy npz files would be helpful.

The data should be stored in the i and q format in the numpy file, e.g.

```py
np.savez_compressed("file.npz", i=np.real(data), q=np.imag(data))

file = np.load("file.npz")
restored_data = file["i"] + 1j * file["q"]

assert np.array_equal(data, restored_data)
```

Additionally, the following parameter for the `Saver` would be beneficial:

- `store_astype` which converts the data type before saving (defaults to `np.float16`)
- `compress` which would use the compressed version of npz


Discussion / hints:

Fix patch:
diff --git a/src/RsWaveform/__init__.py b/src/RsWaveform/__init__.py
index c4ff0fa..974907f 100644
--- a/src/RsWaveform/__init__.py
+++ b/src/RsWaveform/__init__.py
@@ -9,10 +9,10 @@
 import numpy as np
 
 from . import iqtar, iqw, wv
-from .LoadInterface import LoadInterface
+from .load_interface import LoadInterface
 from .meta import Meta
-from .ParentStorage import ParentStorage
-from .SaveInterface import SaveInterface
+from .parent_storage import ParentStorage
+from .save_interface import SaveInterface
 from .utility.dsp import (
     calculate_par,
     calculate_peak,
@@ -192,8 +192,8 @@ def save(
     "RsWaveform",
     "Iqw",
     "IqTar",
-    "LoadInterface",
-    "SaveInterface",
+    "load_interface",
+    "save_interface",
     "calculate_par",
     "calculate_peak",
     "calculate_rms",
diff --git a/src/RsWaveform/iqtar/__init__.py b/src/RsWaveform/iqtar/__init__.py
index 89dd61d..43b56ea 100644
--- a/src/RsWaveform/iqtar/__init__.py
+++ b/src/RsWaveform/iqtar/__init__.py
@@ -1,6 +1,6 @@
 """Iq tar loader and saver implementation."""
 
-from .Load import Load
-from .Save import Save
+from .load import Load
+from .save import Save
 
 __all__ = ["Load", "Save"]
diff --git a/src/RsWaveform/iqtar/Load.py b/src/RsWaveform/iqtar/load.py
similarity index 98%
rename from src/RsWaveform/iqtar/Load.py
rename to src/RsWaveform/iqtar/load.py
index b8e497a..510f452 100644
--- a/src/RsWaveform/iqtar/Load.py
+++ b/src/RsWaveform/iqtar/load.py
@@ -13,9 +13,9 @@
 from RsWaveform.meta import Meta
 
 from ..iqw import Load as LoadIqw
-from ..LoadInterface import LoadInterface
-from ..ParentStorage import ParentStorage
-from ..Storage import Storage
+from ..load_interface import LoadInterface
+from ..parent_storage import ParentStorage
+from ..storage import Storage
 from ..utility.file_handling import read_file_handle, read_file_handle_tar
 from ..utility.meta_utils import map_meta_information_name
 
diff --git a/src/RsWaveform/iqtar/Save.py b/src/RsWaveform/iqtar/save.py
similarity index 97%
rename from src/RsWaveform/iqtar/Save.py
rename to src/RsWaveform/iqtar/save.py
index 5d289a2..d20413f 100644
--- a/src/RsWaveform/iqtar/Save.py
+++ b/src/RsWaveform/iqtar/save.py
@@ -5,11 +5,11 @@
 import typing
 from pathlib import Path
 
-from ..iqw.Save import Save as SaveIqw
+from ..iqw.save import Save as SaveIqw
 from ..meta.defaults import META_IQTAR_DEFAULTS as META_DEFAULTS
-from ..ParentStorage import ParentStorage
-from ..SaveInterface import SaveInterface
-from ..Storage import Storage
+from ..parent_storage import ParentStorage
+from ..save_interface import SaveInterface
+from ..storage import Storage
 from ..utility.file_handling import write_file_handle, write_file_handle_tar
 
 
diff --git a/src/RsWaveform/iqw/__init__.py b/src/RsWaveform/iqw/__init__.py
index 15fd430..3ae6ba1 100644
--- a/src/RsWaveform/iqw/__init__.py
+++ b/src/RsWaveform/iqw/__init__.py
@@ -1,6 +1,6 @@
 """Iqw loader and saver implementation."""
 
-from .Load import Load
-from .Save import Save
+from .load import Load
+from .save import Save
 
 __all__ = ["Load", "Save"]
diff --git a/src/RsWaveform/iqw/Load.py b/src/RsWaveform/iqw/load.py
similarity index 96%
rename from src/RsWaveform/iqw/Load.py
rename to src/RsWaveform/iqw/load.py
index 9361438..7731d17 100644
--- a/src/RsWaveform/iqw/Load.py
+++ b/src/RsWaveform/iqw/load.py
@@ -6,8 +6,8 @@
 
 import numpy as np
 
-from ..LoadInterface import LoadInterface
-from ..ParentStorage import ParentStorage
+from ..load_interface import LoadInterface
+from ..parent_storage import ParentStorage
 from ..utility.fake_jit import jit
 from ..utility.file_handling import read_file_handle
 
diff --git a/src/RsWaveform/iqw/Save.py b/src/RsWaveform/iqw/save.py
similarity index 94%
rename from src/RsWaveform/iqw/Save.py
rename to src/RsWaveform/iqw/save.py
index ffd5f7b..b909f64 100644
--- a/src/RsWaveform/iqw/Save.py
+++ b/src/RsWaveform/iqw/save.py
@@ -7,9 +7,9 @@
 
 import numpy as np
 
-from ..ParentStorage import ParentStorage
-from ..SaveInterface import SaveInterface
-from ..Storage import Storage
+from ..parent_storage import ParentStorage
+from ..save_interface import SaveInterface
+from ..storage import Storage
 from ..utility.fake_jit import jit
 from ..utility.file_handling import write_file_handle
 
diff --git a/src/RsWaveform/LoadInterface.py b/src/RsWaveform/load_interface.py
similarity index 94%
rename from src/RsWaveform/LoadInterface.py
rename to src/RsWaveform/load_interface.py
index f1c7dea..58e312d 100644
--- a/src/RsWaveform/LoadInterface.py
+++ b/src/RsWaveform/load_interface.py
@@ -5,7 +5,7 @@
 import abc
 import typing
 
-from .ParentStorage import ParentStorage
+from .parent_storage import ParentStorage
 
 if typing.TYPE_CHECKING:
     from pathlib import Path
diff --git a/src/RsWaveform/npz/README.md b/src/RsWaveform/npz/README.md
new file mode 100644
index 0000000..5fc4177
--- /dev/null
+++ b/src/RsWaveform/npz/README.md
@@ -0,0 +1,4 @@
+# The Npz loader and saver
+
+The Npz loader and saver comes also with an extended interface providing a `dtype` parameter
+for setting the format of the output `i` and `q` values accordingly for loading and saving.
\ No newline at end of file
diff --git a/src/RsWaveform/npz/__init__.py b/src/RsWaveform/npz/__init__.py
new file mode 100644
index 0000000..eb237ef
--- /dev/null
+++ b/src/RsWaveform/npz/__init__.py
@@ -0,0 +1,6 @@
+"""Npz waveform loader and saver implementation."""
+
+from .load import Load
+from .save import Save
+
+__all__ = ["Load", "Save"]
diff --git a/src/RsWaveform/npz/load.py b/src/RsWaveform/npz/load.py
new file mode 100644
index 0000000..fdcfedf
--- /dev/null
+++ b/src/RsWaveform/npz/load.py
@@ -0,0 +1,90 @@
+"""Load implementation for generic waveform."""
+
+from __future__ import annotations
+
+import logging
+import typing
+
+import numpy as np
+
+from ..load_interface import LoadInterface
+from ..meta import Meta
+from ..parent_storage import ParentStorage
+from ..storage import Storage
+from .npz_inteface import NpzInterface
+
+if typing.TYPE_CHECKING:
+    from pathlib import Path
+
+
+LOGGER = logging.getLogger(__name__)
+
+
+class Load(LoadInterface, NpzInterface):
+    """Load class for generic waveform."""
+
+    def load(self, file: typing.Union[str, typing.IO, Path]) -> ParentStorage:
+        """Load waveform data from npz file."""
+        content = np.load(file=file, allow_pickle=True)
+        data_objs = content.get("storages")
+        if not data_objs:
+            raise ValueError(f"File {file} is not compliant to data schema.")
+        parent = ParentStorage(number_of_storages=len(data_objs))
+        for idx, obj in enumerate(data_objs):
+            i_values = obj.get("i")
+            q_values = obj.get("q")
+            meta = obj.get("meta", {})
+            if i_values is None:
+                LOGGER.warning(
+                    (
+                        "Skipping index %s of file %s content because no i values are"
+                        " in it."
+                    ),
+                    idx,
+                    str(file),
+                )
+                continue
+            if q_values is None:
+                q_values = np.zeros(i_values.size)
+            if q_values is not None and len(i_values) != len(q_values):
+                LOGGER.warning(
+                    (
+                        "Skipping index %s of file %s content because i value and"
+                        " q value lengths are not identical."
+                    ),
+                    idx,
+                    str(file),
+                )
+                continue
+            storage = Storage()
+            storage.data = i_values.astype(self.dtype) + 1j * q_values.astype(
+                self.dtype
+            )
+            storage.meta = Meta(no_defaults=True, **meta)
+            parent.storages[idx] = storage
+        return parent
+
+    def load_in_chunks(
+        self,
+        file: typing.Union[str, typing.IO, Path],
+        samples: int,
+        offset: int,
+    ) -> ParentStorage:
+        """Load chunk waveform data from file."""
+        parent = self.load(file)
+        for idx, storage in enumerate(parent.storages):
+            if offset + samples > len(storage.data):
+                raise ValueError(
+                    f"Could not load chunks for index {idx} of file {file} because "
+                    f"offset {offset} and sample count {samples} is greater than length"
+                    f" of available data {len(storage.data)}. "
+                )
+            storage.data = storage.data[offset : offset + samples]
+        return parent
+
+    def load_meta(self, file: typing.Union[str, typing.IO, Path]) -> ParentStorage:
+        """Load meta information only from wv file."""
+        parent = self.load(file)
+        for storage in parent.storages:
+            storage.data = np.empty((0, 0))
+        return parent
diff --git a/src/RsWaveform/npz/npz_inteface.py b/src/RsWaveform/npz/npz_inteface.py
new file mode 100644
index 0000000..83e3356
--- /dev/null
+++ b/src/RsWaveform/npz/npz_inteface.py
@@ -0,0 +1,30 @@
+"""Npz interface."""
+
+import numpy as np
+import numpy.typing as npt
+
+
+class NpzInterface:
+    """Interface class for Npz."""
+
+    def __init__(self) -> None:
+        """Instantiate interface class."""
+        self._dtype: npt.DTypeLike = np.dtype(np.float16)
+
+    @property
+    def dtype(self) -> npt.DTypeLike:
+        """Dtype for loading and saving.
+
+        Returns:
+            DTypeLike: Numpy dtype
+        """
+        return self._dtype
+
+    @dtype.setter
+    def dtype(self, value: npt.DTypeLike) -> None:
+        """Dtype for loading and saving.
+
+        Args:
+            value (DTypeLike): Numpy dtype
+        """
+        self._dtype = value
diff --git a/src/RsWaveform/npz/save.py b/src/RsWaveform/npz/save.py
new file mode 100644
index 0000000..02248b1
--- /dev/null
+++ b/src/RsWaveform/npz/save.py
@@ -0,0 +1,33 @@
+"""Save implementation for generic waveform."""
+
+from __future__ import annotations
+
+import typing
+
+import numpy as np
+
+from ..parent_storage import ParentStorage
+from ..save_interface import SaveInterface
+from .npz_inteface import NpzInterface
+
+if typing.TYPE_CHECKING:
+    from pathlib import Path
+
+
+class Save(SaveInterface, NpzInterface):
+    """Save class for generic waveform."""
+
+    def save(
+        self,
+        file: typing.Union[str, typing.IO, Path],
+        datas: ParentStorage,
+        scale: float = np.power(2, np.iinfo(np.int16).bits - 1),
+    ) -> None:
+        """Save waveform data to file."""
+        data_objs = []
+        for storage in datas.storages:
+            i_values = np.real(storage.data).astype(self.dtype)
+            q_values = np.imag(storage.data).astype(self.dtype)
+            meta = storage.meta._items  # pylint: disable=W0212
+            data_objs.append({"i": i_values, "q": q_values, "meta": meta})
+        np.savez_compressed(file=file, storages=data_objs)  # type: ignore
diff --git a/src/RsWaveform/ParentStorage.py b/src/RsWaveform/parent_storage.py
similarity index 97%
rename from src/RsWaveform/ParentStorage.py
rename to src/RsWaveform/parent_storage.py
index 816ebd8..9fbbc6d 100644
--- a/src/RsWaveform/ParentStorage.py
+++ b/src/RsWaveform/parent_storage.py
@@ -4,7 +4,7 @@
 import typing
 from datetime import datetime
 
-from .Storage import Storage
+from .storage import Storage
 
 
 class ParentStorage(abc.ABC):
diff --git a/src/RsWaveform/SaveInterface.py b/src/RsWaveform/save_interface.py
similarity index 91%
rename from src/RsWaveform/SaveInterface.py
rename to src/RsWaveform/save_interface.py
index a66301d..d36af01 100644
--- a/src/RsWaveform/SaveInterface.py
+++ b/src/RsWaveform/save_interface.py
@@ -5,7 +5,7 @@
 import abc
 import typing
 
-from .ParentStorage import ParentStorage
+from .parent_storage import ParentStorage
 
 if typing.TYPE_CHECKING:
     from pathlib import Path
diff --git a/src/RsWaveform/Storage.py b/src/RsWaveform/storage.py
similarity index 100%
rename from src/RsWaveform/Storage.py
rename to src/RsWaveform/storage.py
diff --git a/src/RsWaveform/wv/__init__.py b/src/RsWaveform/wv/__init__.py
index af9c7fa..c116aa6 100644
--- a/src/RsWaveform/wv/__init__.py
+++ b/src/RsWaveform/wv/__init__.py
@@ -1,6 +1,6 @@
 """Signal Generator Waveform loader and saver implementation."""
 
-from .Load import Load
-from .Save import Save
+from .load import Load
+from .save import Save
 
 __all__ = ["Load", "Save"]
diff --git a/src/RsWaveform/wv/Load.py b/src/RsWaveform/wv/load.py
similarity index 98%
rename from src/RsWaveform/wv/Load.py
rename to src/RsWaveform/wv/load.py
index f15a9fe..572ccbd 100644
--- a/src/RsWaveform/wv/Load.py
+++ b/src/RsWaveform/wv/load.py
@@ -9,10 +9,10 @@
 
 import numpy as np
 
-from ..LoadInterface import LoadInterface
+from ..load_interface import LoadInterface
 from ..meta import Meta
-from ..ParentStorage import ParentStorage
-from ..Storage import Storage
+from ..parent_storage import ParentStorage
+from ..storage import Storage
 from ..utility.fake_jit import jit
 from ..utility.file_handling import read_file_handle
 from .utility.array_to_bytes import unpack_bytes_to_bool_array
@@ -92,20 +92,20 @@ def _read_chunks(stream: typing.IO, separators: list) -> typing.Tuple[bytes, byt
     def load_in_chunks(
         self,
         file: typing.Union[str, typing.IO, Path],
-        nr_samples: int,
-        samples_offset: int,
+        samples: int,
+        offset: int,
     ) -> ParentStorage:
         """Load chunk waveform data from file."""
         separators = ["{WWAVEFORM", "{WAVEFORM"]
         with read_file_handle(file) as fp:
             header_content, content = self._read_chunks(fp, separators)
-            content += fp.read((nr_samples + 4) * 4 + 100)
+            content += fp.read((samples + 4) * 4 + 100)
             # +4 due to opt words and 100 just an offset to make sure we get everything
 
         tags = self._split_data_via_tags_meta(header_content)
         tags.update(self._split_data_via_tags_waveform(content, True))
         tags.update(
-            self._split_data_via_tags_control_list_width4(content, nr_samples, True)
+            self._split_data_via_tags_control_list_width4(content, samples, True)
         )
 
         mwv_segment_count = int(tags.pop("mwv segment count", 1))
@@ -115,13 +115,13 @@ def load_in_chunks(
         if isinstance(file, str):
             parent_storage.filename = file
         storages = [Storage() for _ in range(mwv_segment_count)]
-        iq_data = self._extract_iq_chunks(tags, nr_samples, samples_offset)
+        iq_data = self._extract_iq_chunks(tags, samples, offset)
         index = 0
         storages[index].data = iq_data
         meta = self._extract_meta(tags, index)
         meta = self._handle_mwv_meta_data(meta, index)
         storages[index].meta = Meta(**meta)
-        if nr_samples != len(iq_data):
+        if samples != len(iq_data):
             raise ValueError(
                 "Sanity problem. Sample setting and actual sample count does not match."
             )
diff --git a/src/RsWaveform/wv/Save.py b/src/RsWaveform/wv/save.py
similarity index 99%
rename from src/RsWaveform/wv/Save.py
rename to src/RsWaveform/wv/save.py
index e0046cc..bbf7f43 100644
--- a/src/RsWaveform/wv/Save.py
+++ b/src/RsWaveform/wv/save.py
@@ -8,9 +8,9 @@
 import numpy as np
 
 from ..meta.defaults import META_WV_DEFAULTS as META_DEFAULTS
-from ..ParentStorage import ParentStorage
-from ..SaveInterface import SaveInterface
-from ..Storage import Storage
+from ..parent_storage import ParentStorage
+from ..save_interface import SaveInterface
+from ..storage import Storage
 from ..utility.dsp import calculate_peak, calculate_rms
 from ..utility.fake_jit import jit
 from ..utility.file_handling import write_file_handle
diff --git a/tox.ini b/tox.ini
index 2db11fd..6e0a9e0 100644
--- a/tox.ini
+++ b/tox.ini
@@ -69,7 +69,6 @@ description = Find errors with static code analysis.
 envdir = {toxworkdir}/lint
 deps =
     flake8
-    flake8-colors
 commands =
     flake8 --select F {posargs:src/ tests/}
 


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index d363b49..a22faae 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -49,3 +49,9 @@ def reference_iqtar_file_name_huge() -> str:
 def reference_iqtar_file_name_multi_channel() -> str:
     dirname = os.path.dirname(__file__)
     return os.path.join(os.path.realpath(dirname), "data", "dummy_multi_channel.iq.tar")
+
+
+@pytest.fixture
+def reference_npz_file_name() -> str:
+    dirname = os.path.dirname(__file__)
+    return os.path.join(os.path.realpath(dirname), "data", "dummy.npz")
diff --git a/tests/data/dummy.npz b/tests/data/dummy.npz
new file mode 100644
index 0000000..6bdb973
Binary files /dev/null and b/tests/data/dummy.npz differ
diff --git a/tests/test_Storage.py b/tests/test_Storage.py
index 96380bd..aacb5e2 100644
--- a/tests/test_Storage.py
+++ b/tests/test_Storage.py
@@ -12,8 +12,8 @@
 import pytest
 
 from RsWaveform.meta import Meta
-from RsWaveform.ParentStorage import ParentStorage
-from RsWaveform.Storage import Storage, encode_datetime, msgpack_import
+from RsWaveform.parent_storage import ParentStorage
+from RsWaveform.storage import Storage, encode_datetime, msgpack_import
 
 
 @pytest.fixture
diff --git a/tests/test_iqtar.py b/tests/test_iqtar.py
index 9ee7cd6..dff6277 100644
--- a/tests/test_iqtar.py
+++ b/tests/test_iqtar.py
@@ -5,11 +5,11 @@
 import pytest
 
 from RsWaveform import IqTar
-from RsWaveform.iqtar.Load import Load
-from RsWaveform.iqtar.Save import Save
+from RsWaveform.iqtar.load import Load
+from RsWaveform.iqtar.save import Save
 from RsWaveform.meta import Meta
-from RsWaveform.ParentStorage import ParentStorage
-from RsWaveform.Storage import Storage
+from RsWaveform.parent_storage import ParentStorage
+from RsWaveform.storage import Storage
 
 
 @pytest.fixture
diff --git a/tests/test_iqw.py b/tests/test_iqw.py
index be95289..8177cde 100644
--- a/tests/test_iqw.py
+++ b/tests/test_iqw.py
@@ -4,10 +4,10 @@
 import pytest
 
 from RsWaveform import Iqw
-from RsWaveform.iqw.Load import Load
-from RsWaveform.iqw.Save import Save
+from RsWaveform.iqw.load import Load
+from RsWaveform.iqw.save import Save
 from RsWaveform.meta import Meta
-from RsWaveform.ParentStorage import ParentStorage
+from RsWaveform.parent_storage import ParentStorage
 
 
 @pytest.fixture
diff --git a/tests/test_npz.py b/tests/test_npz.py
new file mode 100644
index 0000000..b481fcd
--- /dev/null
+++ b/tests/test_npz.py
@@ -0,0 +1,155 @@
+from datetime import datetime
+from unittest.mock import MagicMock, patch
+
+import numpy as np
+import pytest
+
+from RsWaveform.meta import Meta
+from RsWaveform.npz import Load, Save
+from RsWaveform.parent_storage import ParentStorage
+from RsWaveform.storage import Storage
+
+
+@pytest.fixture
+def reference() -> np.ndarray:  # type: ignore
+    return np.array([0.2, 0.6]) + 1j * np.array([0.4, 0.8])
+
+
+@pytest.fixture
+def meta() -> Meta:
+    return Meta(
+        **{  # type: ignore[arg-type]
+            "type": "SMU-WV",
+            "copyright": "Rohde & Schwarz",
+            "comment": "Test waveform file",
+            "control_length": 2,
+            "date": datetime(2023, 1, 5, 10, 3, 52),
+            "clock": 100000000.0,
+            "rms": 2.220459,
+            "peak": 0.0,
+            "samples": 2,
+            "reflevel": -20.0,
+        }
+    )
+
+
+@patch("RsWaveform.npz.save.np")
+def test_save(mock_np: MagicMock, reference, meta):
+    mock_np.real = np.real
+    mock_np.imag = np.imag
+    mock_np.iinfo = np.iinfo
+    parent_storage = ParentStorage()
+    storage = Storage()
+    storage.data = reference
+    storage.meta = meta
+    parent_storage.storages[0] = storage
+    saver = Save()
+    saver.dtype = np.float16
+    filename = "test.npz"
+    saver.save(filename, parent_storage)
+    call_args = mock_np.savez_compressed.call_args[1]
+    assert filename == call_args.get("file")
+    assert np.array_equal(
+        np.real(reference).astype(np.float16), call_args.get("storages")[0]["i"]
+    )
+    assert np.array_equal(
+        np.imag(reference).astype(np.float16), call_args.get("storages")[0]["q"]
+    )
+    assert meta._items == call_args.get("storages")[0]["meta"]  # pylint: disable=W0212
+
+
+def test_load(reference, meta, reference_npz_file_name):
+    loader = Load()
+    loader.dtype = np.float16
+    parent_storage = loader.load(reference_npz_file_name)
+    storage = parent_storage.storages[0]
+    assert np.array_equal(
+        np.real(storage.data).astype(np.float16), np.real(reference).astype(np.float16)
+    )
+    assert np.array_equal(
+        np.imag(storage.data).astype(np.float16), np.imag(reference).astype(np.float16)
+    )
+    assert storage.meta == meta
+
+
+@patch("RsWaveform.npz.load.np")
+def test_load_not_schema(mock_np, reference_npz_file_name):
+    mock_np.load.return_value.get.return_value = None
+    loader = Load()
+    loader.dtype = np.float16
+    with pytest.raises(ValueError):
+        loader.load(reference_npz_file_name)
+
+
+@patch("RsWaveform.npz.load.np")
+def test_load_skip_i_is_none(mock_np, reference_npz_file_name, caplog):
+    mock_np.zeros = np.zeros
+    mock_np.load.return_value.get.return_value = [{"i": None}]
+    loader = Load()
+    loader.dtype = np.float16
+    loader.load(reference_npz_file_name)
+    assert caplog.record_tuples[0] == (
+        "RsWaveform.npz.load",
+        30,
+        (
+            f"Skipping index 0 of file {reference_npz_file_name}"
+            " content because no i values are in it."
+        ),
+    )
+
+
+@patch("RsWaveform.npz.load.np")
+def test_load_q_is_none(mock_np, reference_npz_file_name):
+    mock_np.zeros = np.zeros
+    real_values = np.array([1, 2])
+    mock_np.load.return_value.get.return_value = [{"i": real_values, "q": None}]
+    loader = Load()
+    loader.dtype = np.float16
+    parent_storage = loader.load(reference_npz_file_name)
+    storage = parent_storage.storages[0]
+    assert np.array_equal(real_values, storage.data)
+
+
+@patch("RsWaveform.npz.load.np")
+def test_load_skip_i_and_q_not_equal(mock_np, reference_npz_file_name, caplog):
+    mock_np.zeros = np.zeros
+    mock_np.load.return_value.get.return_value = [
+        {"i": np.array([1, 2]), "q": np.array([3, 4, 5])}
+    ]
+    loader = Load()
+    loader.dtype = np.float16
+    loader.load(reference_npz_file_name)
+    assert caplog.record_tuples[0] == (
+        "RsWaveform.npz.load",
+        30,
+        (
+            f"Skipping index 0 of file {reference_npz_file_name}"
+            " content because i value and q value lengths are not identical."
+        ),
+    )
+
+
+def test_load_in_chunks(reference, meta, reference_npz_file_name):
+    loader = Load()
+    loader.dtype = np.float16
+    parent_storage = loader.load_in_chunks(reference_npz_file_name, 1, 0)
+    storage = parent_storage.storages[0]
+    assert np.array_equal(
+        np.real(storage.data[:1]).astype(np.float16),
+        np.real(reference[:1]).astype(np.float16),
+    )
+    assert np.array_equal(
+        np.imag(storage.data[:1]).astype(np.float16),
+        np.imag(reference[:1]).astype(np.float16),
+    )
+    assert storage.meta == meta
+    with pytest.raises(ValueError):
+        loader.load_in_chunks(reference_npz_file_name, 10, 0)
+
+
+def test_load_meta(meta, reference_npz_file_name):
+    loader = Load()
+    loader.dtype = np.float16
+    parent_storage = loader.load_meta(reference_npz_file_name)
+    storage = parent_storage.storages[0]
+    assert storage.meta == meta
diff --git a/tests/test_wv.py b/tests/test_wv.py
index d914072..e172f98 100644
--- a/tests/test_wv.py
+++ b/tests/test_wv.py
@@ -8,10 +8,10 @@
 
 from RsWaveform import RsWaveform, normalize
 from RsWaveform.meta import Meta
-from RsWaveform.ParentStorage import ParentStorage
-from RsWaveform.Storage import Storage
-from RsWaveform.wv.Load import Load
-from RsWaveform.wv.Save import Save
+from RsWaveform.parent_storage import ParentStorage
+from RsWaveform.storage import Storage
+from RsWaveform.wv.load import Load
+from RsWaveform.wv.save import Save
 
 
 @pytest.fixture