sigima 0.0.1.dev0__py3-none-any.whl → 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sigima/__init__.py +142 -2
- sigima/client/__init__.py +105 -0
- sigima/client/base.py +780 -0
- sigima/client/remote.py +469 -0
- sigima/client/stub.py +814 -0
- sigima/client/utils.py +90 -0
- sigima/config.py +444 -0
- sigima/data/logo/Sigima.svg +135 -0
- sigima/data/tests/annotations.json +798 -0
- sigima/data/tests/curve_fitting/exponential_fit.txt +511 -0
- sigima/data/tests/curve_fitting/gaussian_fit.txt +100 -0
- sigima/data/tests/curve_fitting/piecewiseexponential_fit.txt +1022 -0
- sigima/data/tests/curve_fitting/polynomial_fit.txt +100 -0
- sigima/data/tests/curve_fitting/twohalfgaussian_fit.txt +1000 -0
- sigima/data/tests/curve_formats/bandwidth.txt +201 -0
- sigima/data/tests/curve_formats/boxcar.npy +0 -0
- sigima/data/tests/curve_formats/datetime.txt +1001 -0
- sigima/data/tests/curve_formats/dynamic_parameters.txt +4000 -0
- sigima/data/tests/curve_formats/fw1e2.txt +301 -0
- sigima/data/tests/curve_formats/fwhm.txt +319 -0
- sigima/data/tests/curve_formats/multiple_curves.csv +29 -0
- sigima/data/tests/curve_formats/noised_saw.mat +0 -0
- sigima/data/tests/curve_formats/oscilloscope.csv +111 -0
- sigima/data/tests/curve_formats/other/other2/recursive2.txt +5 -0
- sigima/data/tests/curve_formats/other/recursive1.txt +5 -0
- sigima/data/tests/curve_formats/paracetamol.npy +0 -0
- sigima/data/tests/curve_formats/paracetamol.txt +1010 -0
- sigima/data/tests/curve_formats/paracetamol_dx_dy.csv +1000 -0
- sigima/data/tests/curve_formats/paracetamol_dy.csv +1001 -0
- sigima/data/tests/curve_formats/pulse1.npy +0 -0
- sigima/data/tests/curve_formats/pulse2.npy +0 -0
- sigima/data/tests/curve_formats/simple.txt +5 -0
- sigima/data/tests/curve_formats/spectrum.mca +2139 -0
- sigima/data/tests/curve_formats/square2.npy +0 -0
- sigima/data/tests/curve_formats/step.npy +0 -0
- sigima/data/tests/fabry-perot1.jpg +0 -0
- sigima/data/tests/fabry-perot2.jpg +0 -0
- sigima/data/tests/flower.npy +0 -0
- sigima/data/tests/image_formats/NF 180338201.scor-data +11003 -0
- sigima/data/tests/image_formats/binary_image.npy +0 -0
- sigima/data/tests/image_formats/binary_image.png +0 -0
- sigima/data/tests/image_formats/centroid_test.npy +0 -0
- sigima/data/tests/image_formats/coordinated_text/complex_image.txt +10011 -0
- sigima/data/tests/image_formats/coordinated_text/complex_ref_image.txt +10010 -0
- sigima/data/tests/image_formats/coordinated_text/image.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image2.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_no_unit_no_label.txt +14 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_nan.txt +15 -0
- sigima/data/tests/image_formats/coordinated_text/image_with_unit.txt +14 -0
- sigima/data/tests/image_formats/fiber.csv +480 -0
- sigima/data/tests/image_formats/fiber.jpg +0 -0
- sigima/data/tests/image_formats/fiber.png +0 -0
- sigima/data/tests/image_formats/fiber.txt +480 -0
- sigima/data/tests/image_formats/gaussian_spot_with_noise.npy +0 -0
- sigima/data/tests/image_formats/mr-brain.dcm +0 -0
- sigima/data/tests/image_formats/noised_gaussian.mat +0 -0
- sigima/data/tests/image_formats/sif_reader/nd_lum_image_no_glue.sif +0 -0
- sigima/data/tests/image_formats/sif_reader/raman1.sif +0 -0
- sigima/data/tests/image_formats/tiling.txt +10 -0
- sigima/data/tests/image_formats/uint16.tiff +0 -0
- sigima/data/tests/image_formats/uint8.tiff +0 -0
- sigima/data/tests/laser_beam/TEM00_z_13.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_18.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_23.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_30.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_35.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_40.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_45.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_50.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_55.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_60.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_65.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_70.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_75.jpg +0 -0
- sigima/data/tests/laser_beam/TEM00_z_80.jpg +0 -0
- sigima/enums.py +195 -0
- sigima/io/__init__.py +123 -0
- sigima/io/base.py +311 -0
- sigima/io/common/__init__.py +5 -0
- sigima/io/common/basename.py +164 -0
- sigima/io/common/converters.py +189 -0
- sigima/io/common/objmeta.py +181 -0
- sigima/io/common/textreader.py +58 -0
- sigima/io/convenience.py +157 -0
- sigima/io/enums.py +17 -0
- sigima/io/ftlab.py +395 -0
- sigima/io/image/__init__.py +9 -0
- sigima/io/image/base.py +177 -0
- sigima/io/image/formats.py +1016 -0
- sigima/io/image/funcs.py +414 -0
- sigima/io/signal/__init__.py +9 -0
- sigima/io/signal/base.py +129 -0
- sigima/io/signal/formats.py +290 -0
- sigima/io/signal/funcs.py +723 -0
- sigima/objects/__init__.py +260 -0
- sigima/objects/base.py +937 -0
- sigima/objects/image/__init__.py +88 -0
- sigima/objects/image/creation.py +556 -0
- sigima/objects/image/object.py +524 -0
- sigima/objects/image/roi.py +904 -0
- sigima/objects/scalar/__init__.py +57 -0
- sigima/objects/scalar/common.py +215 -0
- sigima/objects/scalar/geometry.py +502 -0
- sigima/objects/scalar/table.py +784 -0
- sigima/objects/shape.py +290 -0
- sigima/objects/signal/__init__.py +133 -0
- sigima/objects/signal/constants.py +27 -0
- sigima/objects/signal/creation.py +1428 -0
- sigima/objects/signal/object.py +444 -0
- sigima/objects/signal/roi.py +274 -0
- sigima/params.py +405 -0
- sigima/proc/__init__.py +96 -0
- sigima/proc/base.py +381 -0
- sigima/proc/decorator.py +330 -0
- sigima/proc/image/__init__.py +513 -0
- sigima/proc/image/arithmetic.py +335 -0
- sigima/proc/image/base.py +260 -0
- sigima/proc/image/detection.py +519 -0
- sigima/proc/image/edges.py +329 -0
- sigima/proc/image/exposure.py +406 -0
- sigima/proc/image/extraction.py +458 -0
- sigima/proc/image/filtering.py +219 -0
- sigima/proc/image/fourier.py +147 -0
- sigima/proc/image/geometry.py +661 -0
- sigima/proc/image/mathops.py +340 -0
- sigima/proc/image/measurement.py +195 -0
- sigima/proc/image/morphology.py +155 -0
- sigima/proc/image/noise.py +107 -0
- sigima/proc/image/preprocessing.py +182 -0
- sigima/proc/image/restoration.py +235 -0
- sigima/proc/image/threshold.py +217 -0
- sigima/proc/image/transformations.py +393 -0
- sigima/proc/signal/__init__.py +376 -0
- sigima/proc/signal/analysis.py +206 -0
- sigima/proc/signal/arithmetic.py +551 -0
- sigima/proc/signal/base.py +262 -0
- sigima/proc/signal/extraction.py +60 -0
- sigima/proc/signal/features.py +310 -0
- sigima/proc/signal/filtering.py +484 -0
- sigima/proc/signal/fitting.py +276 -0
- sigima/proc/signal/fourier.py +259 -0
- sigima/proc/signal/mathops.py +420 -0
- sigima/proc/signal/processing.py +580 -0
- sigima/proc/signal/stability.py +175 -0
- sigima/proc/title_formatting.py +227 -0
- sigima/proc/validation.py +272 -0
- sigima/tests/__init__.py +7 -0
- sigima/tests/common/__init__.py +0 -0
- sigima/tests/common/arithmeticparam_unit_test.py +26 -0
- sigima/tests/common/basename_unit_test.py +126 -0
- sigima/tests/common/client_unit_test.py +412 -0
- sigima/tests/common/converters_unit_test.py +77 -0
- sigima/tests/common/decorator_unit_test.py +176 -0
- sigima/tests/common/examples_unit_test.py +104 -0
- sigima/tests/common/kernel_normalization_unit_test.py +242 -0
- sigima/tests/common/roi_basic_unit_test.py +73 -0
- sigima/tests/common/roi_geometry_unit_test.py +171 -0
- sigima/tests/common/scalar_builder_unit_test.py +142 -0
- sigima/tests/common/scalar_unit_test.py +991 -0
- sigima/tests/common/shape_unit_test.py +183 -0
- sigima/tests/common/stat_unit_test.py +138 -0
- sigima/tests/common/title_formatting_unit_test.py +338 -0
- sigima/tests/common/tools_coordinates_unit_test.py +60 -0
- sigima/tests/common/transformations_unit_test.py +178 -0
- sigima/tests/common/validation_unit_test.py +205 -0
- sigima/tests/conftest.py +129 -0
- sigima/tests/data.py +998 -0
- sigima/tests/env.py +280 -0
- sigima/tests/guiutils.py +163 -0
- sigima/tests/helpers.py +532 -0
- sigima/tests/image/__init__.py +28 -0
- sigima/tests/image/binning_unit_test.py +128 -0
- sigima/tests/image/blob_detection_unit_test.py +312 -0
- sigima/tests/image/centroid_unit_test.py +170 -0
- sigima/tests/image/check_2d_array_unit_test.py +63 -0
- sigima/tests/image/contour_unit_test.py +172 -0
- sigima/tests/image/convolution_unit_test.py +178 -0
- sigima/tests/image/datatype_unit_test.py +67 -0
- sigima/tests/image/edges_unit_test.py +155 -0
- sigima/tests/image/enclosingcircle_unit_test.py +88 -0
- sigima/tests/image/exposure_unit_test.py +223 -0
- sigima/tests/image/fft2d_unit_test.py +189 -0
- sigima/tests/image/filtering_unit_test.py +166 -0
- sigima/tests/image/geometry_unit_test.py +654 -0
- sigima/tests/image/hough_circle_unit_test.py +147 -0
- sigima/tests/image/imageobj_unit_test.py +737 -0
- sigima/tests/image/morphology_unit_test.py +71 -0
- sigima/tests/image/noise_unit_test.py +57 -0
- sigima/tests/image/offset_correction_unit_test.py +72 -0
- sigima/tests/image/operation_unit_test.py +518 -0
- sigima/tests/image/peak2d_limits_unit_test.py +41 -0
- sigima/tests/image/peak2d_unit_test.py +133 -0
- sigima/tests/image/profile_unit_test.py +159 -0
- sigima/tests/image/projections_unit_test.py +121 -0
- sigima/tests/image/restoration_unit_test.py +141 -0
- sigima/tests/image/roi2dparam_unit_test.py +53 -0
- sigima/tests/image/roi_advanced_unit_test.py +588 -0
- sigima/tests/image/roi_grid_unit_test.py +279 -0
- sigima/tests/image/spectrum2d_unit_test.py +40 -0
- sigima/tests/image/threshold_unit_test.py +91 -0
- sigima/tests/io/__init__.py +0 -0
- sigima/tests/io/addnewformat_unit_test.py +125 -0
- sigima/tests/io/convenience_funcs_unit_test.py +470 -0
- sigima/tests/io/coordinated_text_format_unit_test.py +495 -0
- sigima/tests/io/datetime_csv_unit_test.py +198 -0
- sigima/tests/io/imageio_formats_test.py +41 -0
- sigima/tests/io/ioregistry_unit_test.py +69 -0
- sigima/tests/io/objmeta_unit_test.py +87 -0
- sigima/tests/io/readobj_unit_test.py +130 -0
- sigima/tests/io/readwriteobj_unit_test.py +67 -0
- sigima/tests/signal/__init__.py +0 -0
- sigima/tests/signal/analysis_unit_test.py +135 -0
- sigima/tests/signal/check_1d_arrays_unit_test.py +169 -0
- sigima/tests/signal/convolution_unit_test.py +404 -0
- sigima/tests/signal/datetime_unit_test.py +176 -0
- sigima/tests/signal/fft1d_unit_test.py +303 -0
- sigima/tests/signal/filters_unit_test.py +403 -0
- sigima/tests/signal/fitting_unit_test.py +929 -0
- sigima/tests/signal/fwhm_unit_test.py +111 -0
- sigima/tests/signal/noise_unit_test.py +128 -0
- sigima/tests/signal/offset_correction_unit_test.py +34 -0
- sigima/tests/signal/operation_unit_test.py +489 -0
- sigima/tests/signal/peakdetection_unit_test.py +145 -0
- sigima/tests/signal/processing_unit_test.py +657 -0
- sigima/tests/signal/pulse/__init__.py +112 -0
- sigima/tests/signal/pulse/crossing_times_unit_test.py +123 -0
- sigima/tests/signal/pulse/plateau_detection_unit_test.py +102 -0
- sigima/tests/signal/pulse/pulse_unit_test.py +1824 -0
- sigima/tests/signal/roi_advanced_unit_test.py +392 -0
- sigima/tests/signal/signalobj_unit_test.py +603 -0
- sigima/tests/signal/stability_unit_test.py +431 -0
- sigima/tests/signal/uncertainty_unit_test.py +611 -0
- sigima/tests/vistools.py +1030 -0
- sigima/tools/__init__.py +59 -0
- sigima/tools/checks.py +290 -0
- sigima/tools/coordinates.py +308 -0
- sigima/tools/datatypes.py +26 -0
- sigima/tools/image/__init__.py +97 -0
- sigima/tools/image/detection.py +451 -0
- sigima/tools/image/exposure.py +77 -0
- sigima/tools/image/extraction.py +48 -0
- sigima/tools/image/fourier.py +260 -0
- sigima/tools/image/geometry.py +190 -0
- sigima/tools/image/preprocessing.py +165 -0
- sigima/tools/signal/__init__.py +86 -0
- sigima/tools/signal/dynamic.py +254 -0
- sigima/tools/signal/features.py +135 -0
- sigima/tools/signal/filtering.py +171 -0
- sigima/tools/signal/fitting.py +1171 -0
- sigima/tools/signal/fourier.py +466 -0
- sigima/tools/signal/interpolation.py +70 -0
- sigima/tools/signal/peakdetection.py +126 -0
- sigima/tools/signal/pulse.py +1626 -0
- sigima/tools/signal/scaling.py +50 -0
- sigima/tools/signal/stability.py +258 -0
- sigima/tools/signal/windowing.py +90 -0
- sigima/worker.py +79 -0
- sigima-1.0.0.dist-info/METADATA +233 -0
- sigima-1.0.0.dist-info/RECORD +262 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/licenses/LICENSE +29 -29
- sigima-0.0.1.dev0.dist-info/METADATA +0 -60
- sigima-0.0.1.dev0.dist-info/RECORD +0 -6
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/WHEEL +0 -0
- {sigima-0.0.1.dev0.dist-info → sigima-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Arithmetic parameters unit test.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
|
|
11
|
+
from sigima.params import ArithmeticParam
|
|
12
|
+
from sigima.tests import guiutils
|
|
13
|
+
from sigima.tests.env import execenv
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.mark.gui
|
|
17
|
+
def test_arithmetic_param_interactive():
|
|
18
|
+
"""Arithmetic parameters interactive test."""
|
|
19
|
+
with guiutils.lazy_qt_app_context(force=True):
|
|
20
|
+
param = ArithmeticParam()
|
|
21
|
+
if param.edit():
|
|
22
|
+
execenv.print(param)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
if __name__ == "__main__":
|
|
26
|
+
test_arithmetic_param_interactive()
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""Unit tests for :py:func:`sigima.io.common.basename.format_basenames`."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from sigima.io.common.basename import format_basenames
|
|
10
|
+
from sigima.objects.image import ImageObj
|
|
11
|
+
from sigima.objects.signal import SignalObj
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def make_signal(
|
|
15
|
+
title: str = "",
|
|
16
|
+
xlabel: str = "",
|
|
17
|
+
xunit: str = "",
|
|
18
|
+
ylabel: str = "",
|
|
19
|
+
yunit: str = "",
|
|
20
|
+
metadata: dict | None = None,
|
|
21
|
+
) -> SignalObj:
|
|
22
|
+
"""Create a SignalObj with specified attributes for testing.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
title: Title of the signal.
|
|
26
|
+
xlabel: Label for the x-axis.
|
|
27
|
+
xunit: Unit for the x-axis.
|
|
28
|
+
ylabel: Label for the y-axis.
|
|
29
|
+
yunit: Unit for the y-axis.
|
|
30
|
+
metadata: Metadata dictionary.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Configured SignalObj instance.
|
|
34
|
+
"""
|
|
35
|
+
sig = SignalObj()
|
|
36
|
+
sig.title = title
|
|
37
|
+
sig.xlabel = xlabel
|
|
38
|
+
sig.xunit = xunit
|
|
39
|
+
sig.ylabel = ylabel
|
|
40
|
+
sig.yunit = yunit
|
|
41
|
+
sig.metadata = {} if metadata is None else metadata
|
|
42
|
+
return sig
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def make_image(title: str = "", metadata: dict | None = None) -> ImageObj:
|
|
46
|
+
"""Create an ImageObj with specified attributes for testing.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
title: Title of the image.
|
|
50
|
+
metadata: Metadata dictionary.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
Configured ImageObj instance.
|
|
54
|
+
"""
|
|
55
|
+
img = ImageObj()
|
|
56
|
+
img.title = title
|
|
57
|
+
img.metadata = {} if metadata is None else metadata
|
|
58
|
+
return img
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_format_basenames_with_indices_and_total_count():
|
|
62
|
+
"""Test with indexing and total count placeholders."""
|
|
63
|
+
objs = [make_signal("sig1"), make_signal("sig2"), make_signal("sig3")]
|
|
64
|
+
names = format_basenames(objs, fmt="{index:02d}_of_{count:02d}")
|
|
65
|
+
assert names == ["01_of_03", "02_of_03", "03_of_03"]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def test_format_basenames_with_metadata_and_axes_placeholders():
|
|
69
|
+
"""Test with metadata and axis placeholders."""
|
|
70
|
+
sig = make_signal(
|
|
71
|
+
title="My/Signal",
|
|
72
|
+
xlabel="Time",
|
|
73
|
+
xunit="s",
|
|
74
|
+
ylabel="Amp",
|
|
75
|
+
yunit="V",
|
|
76
|
+
metadata={"id": 42},
|
|
77
|
+
)
|
|
78
|
+
names = format_basenames(
|
|
79
|
+
[sig], fmt="{title}_{xlabel}[{xunit}]_{ylabel}[{yunit}]_{metadata[id]}"
|
|
80
|
+
)
|
|
81
|
+
assert names == ["My_Signal_Time[s]_Amp[V]_42"]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_format_basenames_sanitization():
|
|
85
|
+
"""Test with sanitization for titles."""
|
|
86
|
+
objs = [make_signal("A/B"), make_image("C/D")] # '/' must be sanitized on all OSes
|
|
87
|
+
names = format_basenames(objs, fmt="{title}")
|
|
88
|
+
assert names == ["A_B", "C_D"]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_format_basenames_sanitization_with_custom_replacement():
|
|
92
|
+
"""Test with custom replacement character."""
|
|
93
|
+
objs = [make_signal("a/b"), make_image("c/d")]
|
|
94
|
+
names = format_basenames(objs, fmt="{title}", replacement="-")
|
|
95
|
+
assert names == ["a-b", "c-d"]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_format_basenames_with_unknown_metadata_key():
|
|
99
|
+
"""Test that requesting a missing metadata key raises a KeyError."""
|
|
100
|
+
sig = make_signal(title="T", metadata={"other": 1})
|
|
101
|
+
with pytest.raises(KeyError):
|
|
102
|
+
format_basenames([sig], fmt="{metadata[id]}")
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def test_format_basenames_with_unknown_placeholder():
|
|
106
|
+
"""Test with unknown placeholder should raise a KeyError."""
|
|
107
|
+
with pytest.raises(KeyError):
|
|
108
|
+
format_basenames([make_signal("x")], fmt="{unknown}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_format_basenames_with_direct_metadata_use():
|
|
112
|
+
"""Test that direct {metadata} use returns empty string (silently ignored)."""
|
|
113
|
+
sig = make_signal(title="Test", metadata={"key1": "value1", "key2": "value2"})
|
|
114
|
+
names = format_basenames([sig], fmt="{title} {metadata}")
|
|
115
|
+
# The {metadata} placeholder should be replaced with empty string
|
|
116
|
+
# The trailing space gets sanitized away
|
|
117
|
+
assert names == ["Test"]
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
if __name__ == "__main__":
|
|
121
|
+
test_format_basenames_with_indices_and_total_count()
|
|
122
|
+
test_format_basenames_with_metadata_and_axes_placeholders()
|
|
123
|
+
test_format_basenames_sanitization()
|
|
124
|
+
test_format_basenames_sanitization_with_custom_replacement()
|
|
125
|
+
test_format_basenames_with_unknown_placeholder()
|
|
126
|
+
test_format_basenames_with_direct_metadata_use()
|
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Sigima Client Comprehensive Headless Test
|
|
5
|
+
|
|
6
|
+
This test uses a stub XML-RPC server to emulate DataLab, allowing the test
|
|
7
|
+
to run without requiring a real DataLab instance.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# pylint: disable=invalid-name # Allows short reference names like x, y, ...
|
|
11
|
+
# pylint: disable=duplicate-code
|
|
12
|
+
# guitest: skip
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import os.path as osp
|
|
17
|
+
import tempfile
|
|
18
|
+
from collections.abc import Generator
|
|
19
|
+
from contextlib import contextmanager
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
import pytest
|
|
23
|
+
from guidata.env import execenv
|
|
24
|
+
|
|
25
|
+
from sigima.client.remote import SimpleRemoteProxy
|
|
26
|
+
from sigima.client.stub import datalab_stub_server
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@contextmanager
|
|
30
|
+
def temporary_directory() -> Generator[str, None, None]:
|
|
31
|
+
"""Create a temporary directory and clean-up afterwards"""
|
|
32
|
+
tmp = tempfile.TemporaryDirectory() # pylint: disable=consider-using-with
|
|
33
|
+
try:
|
|
34
|
+
yield tmp.name
|
|
35
|
+
finally:
|
|
36
|
+
try:
|
|
37
|
+
tmp.cleanup()
|
|
38
|
+
except (PermissionError, RecursionError):
|
|
39
|
+
pass
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class RemoteClientTester:
|
|
43
|
+
"""Headless remote client tester class"""
|
|
44
|
+
|
|
45
|
+
SIG_TITLES = ("Oscilloscope", "Digitizer", "Radiometer", "Voltmeter", "Sensor")
|
|
46
|
+
IMA_TITLES = (
|
|
47
|
+
"Camera",
|
|
48
|
+
"Streak Camera",
|
|
49
|
+
"Image Scanner",
|
|
50
|
+
"Laser Beam Profiler",
|
|
51
|
+
"Gated Imaging Camera",
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
def __init__(self):
|
|
55
|
+
"""Initialize the tester"""
|
|
56
|
+
self.datalab = None
|
|
57
|
+
self.log_messages = []
|
|
58
|
+
self.stub_server_port = None
|
|
59
|
+
|
|
60
|
+
def log(self, message: str) -> None:
|
|
61
|
+
"""Log message for debugging"""
|
|
62
|
+
self.log_messages.append(message)
|
|
63
|
+
execenv.print(f"[CLIENT] {message}")
|
|
64
|
+
|
|
65
|
+
def init_cdl_with_stub(self, port: int) -> bool:
|
|
66
|
+
"""Initialize DataLab connection with stub server"""
|
|
67
|
+
try:
|
|
68
|
+
self.stub_server_port = port
|
|
69
|
+
self.datalab = SimpleRemoteProxy(autoconnect=False)
|
|
70
|
+
self.datalab.connect(port=str(port), timeout=1.0, retries=1)
|
|
71
|
+
self.log("✨ Initialized DataLab connection with stub server ✨")
|
|
72
|
+
self.log(f" Communication port: {self.datalab.port}")
|
|
73
|
+
|
|
74
|
+
# Test getting method list
|
|
75
|
+
methods = self.datalab.get_method_list()
|
|
76
|
+
self.log(f" Available methods: {len(methods)} found")
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
except ConnectionRefusedError:
|
|
80
|
+
self.log("🔥 Connection refused 🔥 (Stub server is not ready)")
|
|
81
|
+
return False
|
|
82
|
+
|
|
83
|
+
def init_cdl(self, port: str | None = None) -> bool:
|
|
84
|
+
"""Initialize DataLab connection
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
port: Port to connect to (if None, uses default)
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
self.datalab = SimpleRemoteProxy(autoconnect=False)
|
|
91
|
+
self.datalab.connect(port=port, timeout=1.0, retries=1)
|
|
92
|
+
self.log("✨ Initialized DataLab connection ✨")
|
|
93
|
+
self.log(f" Communication port: {self.datalab.port}")
|
|
94
|
+
|
|
95
|
+
# Test getting method list
|
|
96
|
+
methods = self.datalab.get_method_list()
|
|
97
|
+
self.log(f" Available methods: {len(methods)} found")
|
|
98
|
+
return True
|
|
99
|
+
|
|
100
|
+
except ConnectionRefusedError:
|
|
101
|
+
self.log("🔥 Connection refused 🔥 (DataLab server is not ready)")
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
def close_datalab(self) -> None:
|
|
105
|
+
"""Close DataLab connection"""
|
|
106
|
+
if self.datalab is not None:
|
|
107
|
+
try:
|
|
108
|
+
self.datalab.close_application()
|
|
109
|
+
self.log("🎬 Closed DataLab!")
|
|
110
|
+
except ConnectionRefusedError:
|
|
111
|
+
self.log("Connection lost while closing DataLab")
|
|
112
|
+
finally:
|
|
113
|
+
self.datalab = None
|
|
114
|
+
|
|
115
|
+
def test_connection_management(self) -> None:
|
|
116
|
+
"""Test connection initialization and method listing"""
|
|
117
|
+
# If we already have a connection (from stub server), skip init
|
|
118
|
+
if self.datalab is None:
|
|
119
|
+
assert self.init_cdl(), "Failed to initialize DataLab connection"
|
|
120
|
+
|
|
121
|
+
# Test method listing
|
|
122
|
+
methods = self.datalab.get_method_list()
|
|
123
|
+
assert isinstance(methods, list), "Method list should be a list"
|
|
124
|
+
assert len(methods) > 0, "Should have at least some methods available"
|
|
125
|
+
|
|
126
|
+
# Test basic server info
|
|
127
|
+
version = self.datalab.get_version()
|
|
128
|
+
assert isinstance(version, str), "Version should be a string"
|
|
129
|
+
self.log(f"DataLab version: {version}")
|
|
130
|
+
|
|
131
|
+
def add_test_signals(self) -> None:
|
|
132
|
+
"""Add test signals to DataLab"""
|
|
133
|
+
if self.datalab is None:
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
x = np.linspace(0, 10, 1000)
|
|
137
|
+
signals_data = [
|
|
138
|
+
("Sine", np.sin(x)),
|
|
139
|
+
("Cosine", np.cos(x)),
|
|
140
|
+
("Exponential", np.exp(-x / 5)),
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
for title, y in signals_data:
|
|
144
|
+
success = self.datalab.add_signal(title, x, y)
|
|
145
|
+
assert success, f"Failed to add signal: {title}"
|
|
146
|
+
self.log(f"Added signal: {title}")
|
|
147
|
+
|
|
148
|
+
def add_test_images(self) -> None:
|
|
149
|
+
"""Add test images to DataLab"""
|
|
150
|
+
if self.datalab is None:
|
|
151
|
+
return
|
|
152
|
+
|
|
153
|
+
images_data = [
|
|
154
|
+
("Zeros", np.zeros((100, 100))),
|
|
155
|
+
("Ones", np.ones((100, 100))),
|
|
156
|
+
("Random", np.random.random((100, 100))),
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
for title, z in images_data:
|
|
160
|
+
success = self.datalab.add_image(title, z)
|
|
161
|
+
assert success, f"Failed to add image: {title}"
|
|
162
|
+
self.log(f"Added image: {title}")
|
|
163
|
+
|
|
164
|
+
def test_object_management(self) -> None:
|
|
165
|
+
"""Test object listing, retrieval, and manipulation"""
|
|
166
|
+
if self.datalab is None:
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
# Test with signals
|
|
170
|
+
self.datalab.set_current_panel("signal")
|
|
171
|
+
assert self.datalab.get_current_panel() == "signal"
|
|
172
|
+
|
|
173
|
+
titles = self.datalab.get_object_titles()
|
|
174
|
+
self.log(f"Signal titles: {titles}")
|
|
175
|
+
assert isinstance(titles, list), "Titles should be a list"
|
|
176
|
+
|
|
177
|
+
uuids = self.datalab.get_object_uuids()
|
|
178
|
+
self.log(f"Signal UUIDs: {uuids}")
|
|
179
|
+
assert isinstance(uuids, list), "UUIDs should be a list"
|
|
180
|
+
assert len(titles) == len(uuids), "Should have equal number of titles and UUIDs"
|
|
181
|
+
|
|
182
|
+
if titles:
|
|
183
|
+
# Test getting first object
|
|
184
|
+
obj = self.datalab.get_object(1) # Get first object
|
|
185
|
+
assert obj is not None, "Should be able to retrieve first object"
|
|
186
|
+
assert hasattr(obj, "title"), "Object should have title attribute"
|
|
187
|
+
self.log(f"Retrieved object: {obj.title}")
|
|
188
|
+
|
|
189
|
+
# Test getting object by UUID
|
|
190
|
+
first_uuid = uuids[0]
|
|
191
|
+
obj_by_uuid = self.datalab.get_object(first_uuid)
|
|
192
|
+
assert obj_by_uuid is not None, "Should retrieve object by UUID"
|
|
193
|
+
assert obj_by_uuid.title == obj.title, "Objects should be the same"
|
|
194
|
+
|
|
195
|
+
# Test with images
|
|
196
|
+
self.datalab.set_current_panel("image")
|
|
197
|
+
assert self.datalab.get_current_panel() == "image"
|
|
198
|
+
|
|
199
|
+
img_titles = self.datalab.get_object_titles()
|
|
200
|
+
self.log(f"Image titles: {img_titles}")
|
|
201
|
+
|
|
202
|
+
if img_titles:
|
|
203
|
+
img_obj = self.datalab.get_object(1)
|
|
204
|
+
assert img_obj is not None, "Should retrieve first image"
|
|
205
|
+
self.log(f"Retrieved image: {img_obj.title}")
|
|
206
|
+
|
|
207
|
+
def test_selection_operations(self) -> None:
|
|
208
|
+
"""Test object selection operations"""
|
|
209
|
+
if self.datalab is None:
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
# Test selecting objects
|
|
213
|
+
self.datalab.set_current_panel("signal")
|
|
214
|
+
uuids = self.datalab.get_object_uuids()
|
|
215
|
+
|
|
216
|
+
if uuids:
|
|
217
|
+
# Select first object
|
|
218
|
+
self.datalab.select_objects([uuids[0]])
|
|
219
|
+
selected = self.datalab.get_sel_object_uuids()
|
|
220
|
+
assert uuids[0] in selected, "Should have selected the object"
|
|
221
|
+
self.log(f"Selected object: {uuids[0]}")
|
|
222
|
+
|
|
223
|
+
# Test selecting multiple objects if available
|
|
224
|
+
if len(uuids) > 1:
|
|
225
|
+
self.datalab.select_objects([uuids[0], uuids[1]])
|
|
226
|
+
selected = self.datalab.get_sel_object_uuids()
|
|
227
|
+
assert len(selected) == 2, "Should have selected 2 objects"
|
|
228
|
+
|
|
229
|
+
def test_annotations_and_shapes(self) -> None:
|
|
230
|
+
"""Test annotation and shape operations"""
|
|
231
|
+
if self.datalab is None:
|
|
232
|
+
return
|
|
233
|
+
|
|
234
|
+
# pylint: disable=import-outside-toplevel
|
|
235
|
+
from plotpy.builder import make
|
|
236
|
+
|
|
237
|
+
# Test with images (annotations are more meaningful for images)
|
|
238
|
+
self.datalab.set_current_panel("image")
|
|
239
|
+
uuids = self.datalab.get_object_uuids()
|
|
240
|
+
|
|
241
|
+
if uuids:
|
|
242
|
+
# Add an annotation
|
|
243
|
+
rect = make.annotated_rectangle(10, 10, 50, 50, title="Test Rectangle")
|
|
244
|
+
self.datalab.add_annotations_from_items([rect])
|
|
245
|
+
self.log("Added annotation rectangle")
|
|
246
|
+
|
|
247
|
+
# Retrieve shapes
|
|
248
|
+
shapes = self.datalab.get_object_shapes()
|
|
249
|
+
assert isinstance(shapes, list), "Shapes should be a list"
|
|
250
|
+
self.log(f"Retrieved {len(shapes)} shapes")
|
|
251
|
+
|
|
252
|
+
# Add label
|
|
253
|
+
self.datalab.add_label_with_title("Test Label")
|
|
254
|
+
self.log("Added label with title")
|
|
255
|
+
|
|
256
|
+
def test_file_operations(self) -> None:
|
|
257
|
+
"""Test file save/load operations"""
|
|
258
|
+
if self.datalab is None:
|
|
259
|
+
return
|
|
260
|
+
|
|
261
|
+
with temporary_directory() as tmpdir:
|
|
262
|
+
# Save to HDF5 file
|
|
263
|
+
fname = osp.join(tmpdir, "test_remote.h5")
|
|
264
|
+
self.datalab.save_to_h5_file(fname)
|
|
265
|
+
self.log(f"Saved data to: {fname}")
|
|
266
|
+
assert osp.exists(fname), "HDF5 file should exist"
|
|
267
|
+
|
|
268
|
+
# Clear all data
|
|
269
|
+
self.datalab.reset_all()
|
|
270
|
+
self.log("Reset all data")
|
|
271
|
+
|
|
272
|
+
# Verify data is cleared
|
|
273
|
+
titles = self.datalab.get_object_titles("signal")
|
|
274
|
+
assert len(titles) == 0, "Signal panel should be empty after reset"
|
|
275
|
+
|
|
276
|
+
# Reload from file
|
|
277
|
+
self.datalab.open_h5_files([fname], import_all=True, reset_all=False)
|
|
278
|
+
self.log("Reloaded data from HDF5 file")
|
|
279
|
+
|
|
280
|
+
# Verify data is restored
|
|
281
|
+
titles = self.datalab.get_object_titles("signal")
|
|
282
|
+
assert len(titles) > 0, "Should have signals after reload"
|
|
283
|
+
|
|
284
|
+
def test_computation_operations(self) -> None:
|
|
285
|
+
"""Test computation operations"""
|
|
286
|
+
if self.datalab is None:
|
|
287
|
+
return
|
|
288
|
+
|
|
289
|
+
# Test signal computations
|
|
290
|
+
self.datalab.set_current_panel("signal")
|
|
291
|
+
uuids = self.datalab.get_object_uuids()
|
|
292
|
+
|
|
293
|
+
if uuids:
|
|
294
|
+
# Select first signal
|
|
295
|
+
self.datalab.select_objects([uuids[0]])
|
|
296
|
+
|
|
297
|
+
# Test some basic computations
|
|
298
|
+
try:
|
|
299
|
+
self.datalab.calc("log10")
|
|
300
|
+
self.log("Applied log10 computation")
|
|
301
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
302
|
+
self.log(f"log10 computation failed: {exc}")
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
self.datalab.calc("fft")
|
|
306
|
+
self.log("Applied FFT computation")
|
|
307
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
308
|
+
self.log(f"FFT computation failed: {exc}")
|
|
309
|
+
|
|
310
|
+
def test_group_operations(self) -> None:
|
|
311
|
+
"""Test group operations"""
|
|
312
|
+
if self.datalab is None:
|
|
313
|
+
return
|
|
314
|
+
|
|
315
|
+
# Add a group
|
|
316
|
+
self.datalab.add_group("Test Group", panel="signal")
|
|
317
|
+
self.log("Added test group")
|
|
318
|
+
|
|
319
|
+
# Get group information
|
|
320
|
+
group_info = self.datalab.get_group_titles_with_object_info()
|
|
321
|
+
assert isinstance(group_info, (list, tuple)), (
|
|
322
|
+
"Group info should be a list or tuple"
|
|
323
|
+
)
|
|
324
|
+
assert len(group_info) == 3, "Should have 3 elements (titles, uuids, titles)"
|
|
325
|
+
self.log(f"Groups: {group_info[0]}")
|
|
326
|
+
|
|
327
|
+
def test_metadata_operations(self) -> None:
|
|
328
|
+
"""Test metadata operations"""
|
|
329
|
+
if self.datalab is None:
|
|
330
|
+
return
|
|
331
|
+
|
|
332
|
+
uuids = self.datalab.get_object_uuids()
|
|
333
|
+
if uuids:
|
|
334
|
+
# Select an object
|
|
335
|
+
self.datalab.select_objects([uuids[0]])
|
|
336
|
+
|
|
337
|
+
# Delete metadata
|
|
338
|
+
self.datalab.delete_metadata(refresh_plot=False, keep_roi=False)
|
|
339
|
+
self.log("Deleted metadata")
|
|
340
|
+
|
|
341
|
+
def run_comprehensive_test(self) -> None:
|
|
342
|
+
"""Run all tests in sequence"""
|
|
343
|
+
self.log("Starting comprehensive remote client test")
|
|
344
|
+
|
|
345
|
+
try:
|
|
346
|
+
# Basic connection test
|
|
347
|
+
self.test_connection_management()
|
|
348
|
+
|
|
349
|
+
# Add test data
|
|
350
|
+
self.add_test_signals()
|
|
351
|
+
self.add_test_images()
|
|
352
|
+
|
|
353
|
+
# Test object management
|
|
354
|
+
self.test_object_management()
|
|
355
|
+
|
|
356
|
+
# Test selection operations
|
|
357
|
+
self.test_selection_operations()
|
|
358
|
+
|
|
359
|
+
# Test annotations and shapes
|
|
360
|
+
try:
|
|
361
|
+
self.test_annotations_and_shapes()
|
|
362
|
+
except ImportError:
|
|
363
|
+
self.log("PlotPy not available, skipping annotations and shapes test")
|
|
364
|
+
|
|
365
|
+
# Test computations
|
|
366
|
+
self.test_computation_operations()
|
|
367
|
+
|
|
368
|
+
# Test group operations
|
|
369
|
+
self.test_group_operations()
|
|
370
|
+
|
|
371
|
+
# Test metadata operations
|
|
372
|
+
self.test_metadata_operations()
|
|
373
|
+
|
|
374
|
+
# Test file operations (this will reset data)
|
|
375
|
+
self.test_file_operations()
|
|
376
|
+
|
|
377
|
+
self.log("✅ All tests completed successfully!")
|
|
378
|
+
|
|
379
|
+
finally:
|
|
380
|
+
# Always try to clean up
|
|
381
|
+
try:
|
|
382
|
+
self.datalab.reset_all()
|
|
383
|
+
self.log("Final cleanup: reset all data")
|
|
384
|
+
except Exception: # pylint: disable=broad-except
|
|
385
|
+
pass
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def test_comprehensive_remote_client():
|
|
389
|
+
"""Comprehensive remote client test (pytest version)"""
|
|
390
|
+
# First try with stub server (always available)
|
|
391
|
+
with datalab_stub_server() as port:
|
|
392
|
+
tester = RemoteClientTester()
|
|
393
|
+
if tester.init_cdl_with_stub(port):
|
|
394
|
+
try:
|
|
395
|
+
tester.run_comprehensive_test()
|
|
396
|
+
return # Test passed with stub server
|
|
397
|
+
finally:
|
|
398
|
+
tester.close_datalab()
|
|
399
|
+
|
|
400
|
+
# If stub server test failed, try with real DataLab
|
|
401
|
+
tester = RemoteClientTester()
|
|
402
|
+
if not tester.init_cdl():
|
|
403
|
+
pytest.skip("Neither stub server nor real DataLab server is available")
|
|
404
|
+
|
|
405
|
+
try:
|
|
406
|
+
tester.run_comprehensive_test()
|
|
407
|
+
finally:
|
|
408
|
+
tester.close_datalab()
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
if __name__ == "__main__":
|
|
412
|
+
test_comprehensive_remote_client()
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
Unit tests for I/O conversion functions
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from sigima.io.common import converters
|
|
13
|
+
from sigima.objects.image import ImageObj
|
|
14
|
+
from sigima.objects.signal import SignalObj
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestConvertArrayToValidDType:
|
|
18
|
+
"""Test suite for convert_array_to_valid_dtype function."""
|
|
19
|
+
|
|
20
|
+
def test_int_arrays(self) -> None:
|
|
21
|
+
"""Test conversion of integer numpy arrays."""
|
|
22
|
+
arr = np.array([1.0, 2.0, 3.0], dtype=np.int32)
|
|
23
|
+
result = converters.convert_array_to_valid_dtype(arr, SignalObj.VALID_DTYPES)
|
|
24
|
+
assert isinstance(result, np.ndarray)
|
|
25
|
+
assert result.dtype == np.float32
|
|
26
|
+
np.testing.assert_array_almost_equal(result, arr, decimal=6)
|
|
27
|
+
|
|
28
|
+
arr = np.array([[1, 2, 3], [1.1, 2, 3]], dtype=np.uint32)
|
|
29
|
+
result = converters.convert_array_to_valid_dtype(arr, ImageObj.VALID_DTYPES)
|
|
30
|
+
assert isinstance(result, np.ndarray)
|
|
31
|
+
assert result.dtype == np.uint8
|
|
32
|
+
np.testing.assert_array_almost_equal(result, arr, decimal=6)
|
|
33
|
+
|
|
34
|
+
arr = np.array([[1, 2, 3], [1.1, 2, 1e8]], dtype=np.uint32)
|
|
35
|
+
result = converters.convert_array_to_valid_dtype(arr, ImageObj.VALID_DTYPES)
|
|
36
|
+
assert isinstance(result, np.ndarray)
|
|
37
|
+
assert result.dtype == np.int32
|
|
38
|
+
np.testing.assert_array_almost_equal(result, arr, decimal=6)
|
|
39
|
+
|
|
40
|
+
def test_float_arrays(self) -> None:
|
|
41
|
+
"""Test conversion of float numpy arrays."""
|
|
42
|
+
arr = np.array([1.1, 2.2, 3.3], dtype=np.float32)
|
|
43
|
+
result = converters.convert_array_to_valid_dtype(arr, SignalObj.VALID_DTYPES)
|
|
44
|
+
assert isinstance(result, np.ndarray)
|
|
45
|
+
assert result.dtype == np.float32
|
|
46
|
+
np.testing.assert_array_almost_equal(result, arr, decimal=6)
|
|
47
|
+
|
|
48
|
+
arr = np.array([[1.1, 2.2, 3.3], [1.1, 2.2, 3.3]], dtype=np.float64)
|
|
49
|
+
result = converters.convert_array_to_valid_dtype(arr, ImageObj.VALID_DTYPES)
|
|
50
|
+
assert isinstance(result, np.ndarray)
|
|
51
|
+
assert result.dtype == np.float64
|
|
52
|
+
np.testing.assert_array_almost_equal(result, arr, decimal=6)
|
|
53
|
+
|
|
54
|
+
def test_bool_arrays(self) -> None:
|
|
55
|
+
"""Test conversion of boolean numpy arrays."""
|
|
56
|
+
arr = np.array([True, False, True], dtype=np.bool_)
|
|
57
|
+
result = converters.convert_array_to_valid_dtype(arr, SignalObj.VALID_DTYPES)
|
|
58
|
+
assert isinstance(result, np.ndarray)
|
|
59
|
+
assert result.dtype == np.uint8
|
|
60
|
+
|
|
61
|
+
def test_empty_arrays(self) -> None:
|
|
62
|
+
"""Test conversion of empty numpy arrays."""
|
|
63
|
+
arr = np.array([], dtype=np.float32)
|
|
64
|
+
result = converters.convert_array_to_valid_dtype(arr, SignalObj.VALID_DTYPES)
|
|
65
|
+
assert isinstance(result, np.ndarray)
|
|
66
|
+
assert result.size == 0
|
|
67
|
+
|
|
68
|
+
def test_invalid_input_type(self) -> None:
|
|
69
|
+
"""Test that invalid input types raise TypeError."""
|
|
70
|
+
with pytest.raises(TypeError):
|
|
71
|
+
converters.convert_array_to_valid_dtype(
|
|
72
|
+
"not an array", SignalObj.VALID_DTYPES
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
if __name__ == "__main__":
|
|
77
|
+
pytest.main([__file__])
|