Public Access
Merge branch 'main' of https://gitea.pkm.physik.tu-darmstadt.de/IPKM/python3-damaris
This commit is contained in:
commit
9e42205a4d
48 files changed
+2184
-717
No files matched your search
+4
-3
@@ -11,6 +11,7 @@ def test(i):
|
||||
return e
|
||||
|
||||
def experiment():
|
||||
for i in range(1400):
|
||||
sleep(0.1)
|
||||
yield test(i)
|
||||
for i in range(100):
|
||||
#sleep(0.1)
|
||||
yield test(i)
|
||||
synchronize()
|
||||
+16
-1
@@ -1,6 +1,6 @@
|
||||
import tables
|
||||
import numpy
|
||||
|
||||
# from damaris import FitResult
|
||||
r = MeasurementResult("test")
|
||||
|
||||
def result():
|
||||
@@ -9,14 +9,29 @@ def result():
|
||||
except:
|
||||
print("h5 already opened")
|
||||
accu = Accumulation(error=False)
|
||||
|
||||
for num,ts in enumerate(results):
|
||||
data["accu"] = accu
|
||||
data["data/ts %i"%num] = ts+0
|
||||
ts.write_to_hdf(h5, "/", f"adc_{num}", f"adc_{num}")
|
||||
data["fft"] = (ts+0).fft()
|
||||
r[num] = ts.y[0][0]
|
||||
if num > 4:
|
||||
x = r.get_xdata()
|
||||
y = r.get_ydata()
|
||||
coeffs = numpy.polyfit(x, y, 2)
|
||||
x_line = numpy.linspace(x.min(), x.max(), 100)
|
||||
y_line = numpy.polyval(coeffs, x_line)
|
||||
|
||||
fit = FitResult(x=x_line,
|
||||
y=y_line,
|
||||
title="Magnetization(tau) fit",
|
||||
source="test",
|
||||
params={"a": coeffs[0], "b": coeffs[1], "c": coeffs[2]})
|
||||
data["test/fit"] = fit
|
||||
data["test"] = r
|
||||
accu+=ts
|
||||
print(accu)
|
||||
accu.write_to_hdf(h5, "/", "name", "title")
|
||||
try:
|
||||
h5.close()
|
||||
|
||||
+64
-11
@@ -195,14 +195,14 @@ class TestADCResult(unittest.TestCase):
|
||||
np.testing.assert_array_equal(adc.y[i]+10, y[i] + 10, strict=True)
|
||||
adc += 10
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] + 10, strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] + 10).astype("float32"), strict=True)
|
||||
|
||||
# test float addition
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i] + 10., y[i] + 10., strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i] + 10., (y[i] + 10 + 10.).astype("float32"), strict=True)
|
||||
adc += 10.
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] + 10 + 10., strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] + 10 + 10.).astype("float32"), strict=True)
|
||||
|
||||
def test_operator_sub_scalar(self):
|
||||
"""
|
||||
@@ -215,14 +215,14 @@ class TestADCResult(unittest.TestCase):
|
||||
np.testing.assert_array_equal(adc.y[i] - 10, y[i] - 10, strict=True)
|
||||
adc -= 10
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] - 10, strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] - 10).astype("float32"), strict=True)
|
||||
|
||||
# test float subtraction
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i] - 10., y[i] - 10., strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i] - 10., (y[i] - 10 - 10.).astype("float32"), strict=True)
|
||||
adc -= 10.
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] - 10 - 10., strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] - 10 - 10.).astype("float32"), strict=True)
|
||||
|
||||
def test_operator_mul_scalar(self):
|
||||
"""
|
||||
@@ -236,14 +236,14 @@ class TestADCResult(unittest.TestCase):
|
||||
np.testing.assert_array_equal(adc.y[i] * 10, y[i] * 10, strict=True)
|
||||
adc *= 10
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] * 10, strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] * 10).astype("float32"), strict=True)
|
||||
|
||||
# test float multiplication
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i] * 10.0, y[i] * 10.0, strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i] * 10.0, (y[i] * 10 * 10.0).astype("float32"), strict=True)
|
||||
adc *= 10.0
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] * 10 * 10.0, strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] * 10 * 10.0).astype("float32"), strict=True)
|
||||
|
||||
def test_operator_truediv_scalar(self):
|
||||
"""
|
||||
@@ -255,7 +255,7 @@ class TestADCResult(unittest.TestCase):
|
||||
np.testing.assert_array_equal(adc.y[i]/10, y[i] / 10, strict=True)
|
||||
adc /= 10
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] / 10, strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] / 10).astype("float32"), strict=True)
|
||||
|
||||
def test_operator_floordiv_scalar(self):
|
||||
"""
|
||||
@@ -267,10 +267,37 @@ class TestADCResult(unittest.TestCase):
|
||||
np.testing.assert_array_equal(adc.y[i]//10, y[i] // 10, strict=True)
|
||||
adc //= 10
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(adc.y[i], y[i] // 10, strict=True)
|
||||
np.testing.assert_array_equal(adc.y[i], (y[i] // 10).astype("float32"), strict=True)
|
||||
self.assertRaises(ValueError, adc.__floordiv__, 1.0)
|
||||
self.assertRaises(ValueError, adc.__rfloordiv__, 1.0)
|
||||
|
||||
def test_operators_keep_channel_metadata(self):
|
||||
"""operator results keep nChannels/is_clipped identical to the operand"""
|
||||
adc = self.create_adc_result()
|
||||
adc.is_clipped = True
|
||||
results = [adc + 1, adc - 1, adc * 2, adc ** 2, adc / 2., adc // 2,
|
||||
10 + adc, 10 - adc, 2 * adc, 100. / adc, 100 // adc, -adc]
|
||||
for r in results:
|
||||
self.assertEqual(r.get_nChannels(), adc.get_nChannels())
|
||||
self.assertEqual(r.is_clipped, adc.is_clipped)
|
||||
|
||||
def test_hdf_roundtrip_keeps_nchannels(self):
|
||||
"""read_from_hdf must restore the number of channels"""
|
||||
import tempfile
|
||||
import tables
|
||||
from damaris.data.ADC_Result import read_from_hdf
|
||||
|
||||
adc = self.create_adc_result()
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "adc.h5")
|
||||
with tables.open_file(path, "w") as h5:
|
||||
adc.write_to_hdf(h5, h5.root, "adc", "test")
|
||||
with tables.open_file(path, "r") as h5:
|
||||
adc2 = read_from_hdf(h5.root.adc)
|
||||
self.assertIsNotNone(adc2)
|
||||
self.assertTrue(adc2.contains_data())
|
||||
self.assertEqual(adc2.get_nChannels(), adc.get_nChannels())
|
||||
|
||||
def test_operator_overload_adc_result_exception(self):
|
||||
"""
|
||||
Test the ValueError raised when ADC_Result is used as an operand in an arithmetic operation
|
||||
@@ -289,5 +316,31 @@ class TestADCResult(unittest.TestCase):
|
||||
self.assertRaises(ValueError, adc1.__rfloordiv__, adc2)
|
||||
self.assertRaises(ValueError, adc1.__pow__, adc2)
|
||||
|
||||
def test_empty_state_types(self):
|
||||
"""Empty ADC_Result must hold an ndarray x and a list-of-ndarray y."""
|
||||
adc = ADC_Result()
|
||||
self.assertIsInstance(adc.x, np.ndarray)
|
||||
self.assertIsInstance(adc.y, list)
|
||||
self.assertEqual(adc.x.size, 0)
|
||||
self.assertEqual(adc.get_xmin(), 0)
|
||||
self.assertEqual(adc.get_xmax(), 0)
|
||||
self.assertEqual(adc.get_xminpos(), 0)
|
||||
self.assertEqual(adc.get_ymin(), 0)
|
||||
self.assertEqual(adc.get_ymax(), 0)
|
||||
self.assertEqual(adc.get_yminpos(), 0)
|
||||
|
||||
def test_constructor_coerces_lists(self):
|
||||
"""Plain lists passed to the constructor must become numpy arrays."""
|
||||
x = [0.0, 1.0, 2.0]
|
||||
y = [[10, 20, 30], [15, 25, 35]]
|
||||
index = [(0, 2)]
|
||||
adc = ADC_Result(x, y, index, 1000.0, {"key": "value"}, 1, datetime(2023, 1, 1))
|
||||
self.assertIsInstance(adc.x, np.ndarray)
|
||||
np.testing.assert_array_equal(adc.x, [0.0, 1.0, 2.0])
|
||||
for ch in adc.y:
|
||||
self.assertIsInstance(ch, np.ndarray)
|
||||
np.testing.assert_array_equal(adc.y[0], [10, 20, 30])
|
||||
np.testing.assert_array_equal(adc.y[1], [15, 25, 35])
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
+249
-1
@@ -1,5 +1,6 @@
|
||||
import os
|
||||
import subprocess
|
||||
import threading
|
||||
import unittest
|
||||
from datetime import datetime
|
||||
|
||||
@@ -258,7 +259,7 @@ class TestAccumulation(unittest.TestCase):
|
||||
np.testing.assert_array_equal(accu.y[i], y[i] - 10)
|
||||
|
||||
# test float subtraction
|
||||
adc -= 10.
|
||||
accu -= 10.
|
||||
for i in range(adc.get_nChannels()):
|
||||
np.testing.assert_array_equal(accu.y[i], y[i] - 10 - 10.)
|
||||
|
||||
@@ -312,5 +313,252 @@ class TestAccumulation(unittest.TestCase):
|
||||
self.assertRaises(ValueError, accu.__floordiv__, 1.0)
|
||||
self.assertRaises(ValueError, accu.__ifloordiv__, 1.0)
|
||||
|
||||
def test_add_invalid_operand_raises(self):
|
||||
"""accu + bogus must raise ValueError instead of returning None."""
|
||||
adc = self.create_adc_result()
|
||||
accu = Accumulation()
|
||||
accu += adc
|
||||
self.assertRaises(ValueError, accu.__add__, "foo")
|
||||
self.assertRaises(ValueError, accu.__iadd__, "foo")
|
||||
|
||||
def test_neg_empty_raises(self):
|
||||
"""-empty accumulation must raise ValueError."""
|
||||
self.assertRaises(ValueError, Accumulation().__neg__)
|
||||
|
||||
def test_add_adc_result_does_not_modify_job_ids(self):
|
||||
"""accu + adc must not insert the adc job id into accu.job_ids."""
|
||||
accu = Accumulation()
|
||||
accu += self.create_adc_result()
|
||||
n_before = len(accu.job_ids)
|
||||
adc2 = self.create_adc_result()
|
||||
adc2.job_id = 12
|
||||
r = accu + adc2
|
||||
self.assertIn(12, r.job_ids)
|
||||
self.assertNotIn(12, accu.job_ids)
|
||||
self.assertEqual(len(accu.job_ids), n_before)
|
||||
|
||||
def test_empty_plus_accumulation_copies_data(self):
|
||||
"""empty_accu + accu must copy arrays and metadata, not alias them."""
|
||||
adc = self.create_adc_result()
|
||||
accu = Accumulation()
|
||||
accu += adc
|
||||
accu.is_clipped = True
|
||||
|
||||
r = Accumulation() + accu
|
||||
self.assertIsNot(r.y[0], accu.y[0])
|
||||
self.assertIsNot(r.job_ids, accu.job_ids)
|
||||
self.assertTrue(r.is_clipped)
|
||||
# in-place operation on the result must leave the operand unchanged
|
||||
r *= 2
|
||||
np.testing.assert_array_equal(accu.y[0], np.array([10, 20, 30], dtype="float32"))
|
||||
|
||||
def test_accumulation_plus_accumulation_keeps_both_job_ids(self):
|
||||
"""accu1 + accu2 must keep the provenance of both operands."""
|
||||
adc1 = self.create_adc_result()
|
||||
adc2 = self.create_adc_result()
|
||||
adc2.job_id = 12
|
||||
accu1 = Accumulation()
|
||||
accu1 += adc1
|
||||
accu2 = Accumulation()
|
||||
accu2 += adc2
|
||||
r = accu1 + accu2
|
||||
self.assertEqual(set(r.job_ids.keys()), {11, 12})
|
||||
self.assertEqual(set(accu1.job_ids.keys()), {11})
|
||||
self.assertEqual(set(accu2.job_ids.keys()), {12})
|
||||
|
||||
def test_empty_error_plus_non_error_raises(self):
|
||||
"""error accumulation + non-error accumulation must raise ValueError."""
|
||||
accu = Accumulation()
|
||||
accu += self.create_adc_result()
|
||||
empty_err = Accumulation(error=True)
|
||||
self.assertRaises(ValueError, empty_err.__add__, accu)
|
||||
free = []
|
||||
def check_lock():
|
||||
if empty_err.lock.acquire(timeout=1):
|
||||
free.append(True)
|
||||
empty_err.lock.release()
|
||||
t = threading.Thread(target=check_lock)
|
||||
t.start()
|
||||
t.join(3)
|
||||
self.assertTrue(free, "lock leaked after ValueError in __add__")
|
||||
|
||||
def test_neg_keeps_y_square_as_list(self):
|
||||
"""-accu must store y_square as a list of per-channel arrays."""
|
||||
x = np.array([0.0, 1.0, 2.0])
|
||||
y = [np.array([10, 20, 30], dtype="float32")]
|
||||
y_square = [np.array([100.0, 400.0, 900.0], dtype="float32")]
|
||||
accu = Accumulation(x=x, y=y, y_2=y_square, n=2, index=[(0, 2)], sampl_freq=1000.0, error=True)
|
||||
|
||||
r = -accu
|
||||
|
||||
self.assertIsInstance(r.y_square, list)
|
||||
# sums of squares are invariant under negation
|
||||
np.testing.assert_array_equal(r.get_ysquare(0), y_square[0])
|
||||
|
||||
def test_iadd_adc_after_ndarray_y_2(self):
|
||||
"""empty accu with ndarray y_square += adc must not crash (was AttributeError)."""
|
||||
y_square_2d = np.array([[100.0, 400.0, 900.0]], dtype="float32")
|
||||
accu = Accumulation(y_2=y_square_2d, error=True)
|
||||
|
||||
accu += self.create_adc_result()
|
||||
|
||||
self.assertIsInstance(accu.y_square, list)
|
||||
np.testing.assert_array_equal(accu.get_ydata(0), np.array([10, 20, 30], dtype="float32"))
|
||||
np.testing.assert_array_equal(accu.get_ysquare(0), np.array([10, 20, 30], dtype="float32") ** 2)
|
||||
|
||||
def test_iadd_accumulation_after_ndarray_y_2(self):
|
||||
"""empty accu with ndarray y_square += accu must not crash (was AttributeError)."""
|
||||
full = Accumulation(error=True)
|
||||
full += self.create_adc_result()
|
||||
y_square_2d = np.array([[100.0, 400.0, 900.0]], dtype="float32")
|
||||
accu = Accumulation(y_2=y_square_2d, error=True)
|
||||
|
||||
accu += full
|
||||
|
||||
self.assertIsInstance(accu.y_square, list)
|
||||
np.testing.assert_array_equal(accu.get_ydata(0), np.array([10, 20, 30], dtype="float32"))
|
||||
np.testing.assert_array_equal(accu.get_ysquare(0), np.array([10, 20, 30], dtype="float32") ** 2)
|
||||
|
||||
def test_constructor_normalizes_ndarray_y_2(self):
|
||||
"""an ndarray passed as y_2 must be stored as a list of channel arrays."""
|
||||
x = np.array([0.0, 1.0, 2.0])
|
||||
y = [np.array([10, 20, 30], dtype="float32")]
|
||||
y_square_2d = np.array([[100.0, 400.0, 900.0]], dtype="float32")
|
||||
|
||||
accu = Accumulation(x=x, y=y, y_2=y_square_2d, n=2, index=[(0, 2)], sampl_freq=1000.0, error=True)
|
||||
|
||||
self.assertIsInstance(accu.y_square, list)
|
||||
np.testing.assert_array_equal(accu.get_ysquare(0), y_square_2d[0])
|
||||
|
||||
def test_add_accumulations_without_time_period(self):
|
||||
"""accu + accu without time information must not crash."""
|
||||
accu1 = Accumulation()
|
||||
accu1 += self.create_adc_result()
|
||||
accu2 = Accumulation()
|
||||
accu2 += self.create_adc_result()
|
||||
accu1.time_period = None
|
||||
accu2.time_period = []
|
||||
|
||||
r = accu1 + accu2
|
||||
|
||||
self.assertIsNone(r.time_period)
|
||||
|
||||
def test_iadd_accumulation_without_time_period(self):
|
||||
"""accu += accu without time information must not crash (was IndexError)."""
|
||||
accu1 = Accumulation()
|
||||
accu1 += self.create_adc_result()
|
||||
accu2 = Accumulation()
|
||||
accu2 += self.create_adc_result()
|
||||
accu1.time_period = []
|
||||
accu2.time_period = []
|
||||
|
||||
accu1 += accu2
|
||||
|
||||
self.assertIsNone(accu1.time_period)
|
||||
|
||||
def test_add_adc_without_time_period(self):
|
||||
"""accu + adc without time information must not crash."""
|
||||
accu = Accumulation()
|
||||
accu += self.create_adc_result()
|
||||
accu.time_period = []
|
||||
|
||||
r = accu + self.create_adc_result()
|
||||
|
||||
self.assertIsNone(r.time_period)
|
||||
|
||||
def test_time_period_merged_on_add(self):
|
||||
"""accu + accu with both time periods set merges to min/max."""
|
||||
accu1 = Accumulation()
|
||||
accu1 += self.create_adc_result()
|
||||
accu2 = Accumulation()
|
||||
accu2 += self.create_adc_result()
|
||||
accu1.time_period = [datetime(2023, 1, 3), datetime(2023, 1, 5)]
|
||||
accu2.time_period = [datetime(2023, 1, 2), datetime(2023, 1, 7)]
|
||||
|
||||
r = accu1 + accu2
|
||||
|
||||
self.assertEqual(r.time_period, [datetime(2023, 1, 2), datetime(2023, 1, 7)])
|
||||
|
||||
def test_time_period_merged_with_adc_job_date(self):
|
||||
"""accu + adc merges self.time_period with other.job_date."""
|
||||
accu = Accumulation()
|
||||
accu += self.create_adc_result()
|
||||
accu.time_period = [datetime(2023, 1, 3), datetime(2023, 1, 5)]
|
||||
adc = self.create_adc_result()
|
||||
adc.job_date = datetime(2023, 1, 1)
|
||||
|
||||
r = accu + adc
|
||||
|
||||
self.assertEqual(r.time_period, [datetime(2023, 1, 1), datetime(2023, 1, 5)])
|
||||
|
||||
def test_iadd_adc_without_job_date_sets_time_period_none(self):
|
||||
"""empty accu += adc without job_date gets time_period None."""
|
||||
accu = Accumulation()
|
||||
adc = self.create_adc_result()
|
||||
adc.job_date = None
|
||||
|
||||
accu += adc
|
||||
|
||||
self.assertIsNone(accu.time_period)
|
||||
|
||||
def test_get_accu_by_index_without_time_period(self):
|
||||
"""get_accu_by_index without time information must not crash."""
|
||||
accu = Accumulation()
|
||||
adc = self.create_adc_result()
|
||||
adc.index = [(0, 1), (1, 2)]
|
||||
accu += adc
|
||||
accu.time_period = None
|
||||
|
||||
r = accu.get_accu_by_index(0)
|
||||
|
||||
self.assertIsNone(r.time_period)
|
||||
|
||||
def test_write_to_tecmag_without_time_period_raises(self):
|
||||
"""write_to_tecmag without time information must raise ValueError."""
|
||||
accu = Accumulation()
|
||||
accu += self.create_adc_result()
|
||||
accu.time_period = None
|
||||
|
||||
self.assertRaises(ValueError, accu.write_to_tecmag, "/tmp/damaris_tecmag_no_time.tnt")
|
||||
self.assertTrue(accu.lock.acquire(timeout=1), "lock leaked after ValueError in write_to_tecmag")
|
||||
accu.lock.release()
|
||||
|
||||
def test_empty_state_accessors(self):
|
||||
"""Empty Accumulation accessors must return ndarrays, min/max helpers 0."""
|
||||
accu = Accumulation()
|
||||
self.assertIsInstance(accu.x, np.ndarray)
|
||||
self.assertEqual(accu.x.size, 0)
|
||||
self.assertIsInstance(accu.get_ydata(), np.ndarray)
|
||||
self.assertEqual(accu.get_ydata().size, 0)
|
||||
self.assertIsInstance(accu.get_yerr(0), np.ndarray)
|
||||
self.assertEqual(accu.get_yerr(0).size, 0)
|
||||
self.assertEqual(accu.get_xmin(), 0)
|
||||
self.assertEqual(accu.get_xmax(), 0)
|
||||
self.assertEqual(accu.get_ymin(), 0)
|
||||
self.assertEqual(accu.get_ymax(), 0)
|
||||
|
||||
def test_get_yerr_without_statistics_on_empty(self):
|
||||
"""get_yerr on an empty accumulation without statistics must not crash."""
|
||||
accu = Accumulation(error=False)
|
||||
err = accu.get_yerr(0)
|
||||
self.assertIsInstance(err, np.ndarray)
|
||||
self.assertEqual(err.size, 0)
|
||||
|
||||
def test_constructor_coerces_lists(self):
|
||||
"""Plain lists passed to the constructor must become numpy arrays."""
|
||||
x = [0.0, 1.0, 2.0]
|
||||
y = [[10, 20, 30], [15, 25, 35]]
|
||||
y_2 = [[100.0, 400.0, 900.0], [225.0, 625.0, 1225.0]]
|
||||
accu = Accumulation(x=x, y=y, y_2=y_2, n=1,
|
||||
index=[(0, 2)], sampl_freq=1000.0, error=True)
|
||||
self.assertIsInstance(accu.x, np.ndarray)
|
||||
np.testing.assert_array_equal(accu.x, [0.0, 1.0, 2.0])
|
||||
for ch in accu.y:
|
||||
self.assertIsInstance(ch, np.ndarray)
|
||||
for ch in accu.y_square:
|
||||
self.assertIsInstance(ch, np.ndarray)
|
||||
np.testing.assert_array_equal(accu.y[0], [10, 20, 30])
|
||||
np.testing.assert_array_equal(accu.y_square[0], [100.0, 400.0, 900.0])
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,29 @@
|
||||
import unittest
|
||||
|
||||
from damaris.data.Config_Result import Config_Result
|
||||
|
||||
|
||||
class TestConfigResult(unittest.TestCase):
|
||||
def test_constructor_with_no_arguments(self):
|
||||
"""Test initializer with no arguments."""
|
||||
cr = Config_Result()
|
||||
self.assertEqual(cr.get_config_dictionary(), {})
|
||||
self.assertEqual(cr.description, {})
|
||||
|
||||
def test_constructor_with_arguments(self):
|
||||
"""Test initializer with config and description arguments."""
|
||||
config = {"a": 1}
|
||||
desc = {"b": 2}
|
||||
cr = Config_Result(config=config, desc=desc)
|
||||
self.assertEqual(cr.get_config_dictionary(), config)
|
||||
self.assertEqual(cr.description, desc)
|
||||
|
||||
def test_get_set_config(self):
|
||||
cr = Config_Result()
|
||||
cr.set_config("key", "value")
|
||||
self.assertEqual(cr.get_config("key"), "value")
|
||||
self.assertIsNone(cr.get_config("missing"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,44 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from damaris.data.DamarisFFT import DamarisFFT
|
||||
|
||||
|
||||
class TestDamarisFFT(unittest.TestCase):
|
||||
@staticmethod
|
||||
def make_fft():
|
||||
fft = DamarisFFT()
|
||||
fft.sampling_rate = 100.0
|
||||
fft.x = np.arange(10, dtype="float32") / 100.0
|
||||
fft.y = [np.arange(10, dtype="float32")]
|
||||
fft.xlabel = "Time / s"
|
||||
return fft
|
||||
|
||||
def test_clip_without_arguments_is_placeholder(self):
|
||||
"""clip() without arguments returns self unchanged (placeholder use)."""
|
||||
fft = self.make_fft()
|
||||
self.assertIs(fft.clip(), fft)
|
||||
self.assertEqual(len(fft.x), 10)
|
||||
np.testing.assert_array_equal(fft.y[0], np.arange(10, dtype="float32"))
|
||||
|
||||
def test_clip_swaps_reversed_bounds(self):
|
||||
fft = self.make_fft()
|
||||
fft.clip(start=0.05, stop=0.01)
|
||||
expected_x = (np.arange(10, dtype="float32") / 100.0)[1:5]
|
||||
np.testing.assert_array_equal(fft.x, expected_x)
|
||||
np.testing.assert_array_equal(fft.y[0], np.arange(1, 5, dtype="float32"))
|
||||
|
||||
def test_clip_single_bound(self):
|
||||
fft = self.make_fft()
|
||||
fft.clip(start=0.02)
|
||||
self.assertEqual(len(fft.x), 7)
|
||||
self.assertEqual(len(fft.y[0]), 7)
|
||||
|
||||
def test_clip_too_close_raises(self):
|
||||
fft = self.make_fft()
|
||||
self.assertRaises(ValueError, fft.clip, 0.021, 0.029)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,54 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from damaris.data.Drawable import Drawable
|
||||
|
||||
|
||||
class TestDrawable(unittest.TestCase):
|
||||
def test_initial_types(self):
|
||||
"""x must start as an empty ndarray, y as a list of arrays."""
|
||||
d = Drawable()
|
||||
self.assertIsInstance(d.x, np.ndarray)
|
||||
self.assertEqual(d.x.size, 0)
|
||||
self.assertIsInstance(d.y, list)
|
||||
|
||||
def test_empty_min_max_helpers(self):
|
||||
"""min/max helpers must return 0 instead of raising on empty data."""
|
||||
d = Drawable()
|
||||
self.assertEqual(d.get_xmin(), 0)
|
||||
self.assertEqual(d.get_xmax(), 0)
|
||||
self.assertEqual(d.get_xminpos(), 0)
|
||||
self.assertEqual(d.get_ymin(), 0)
|
||||
self.assertEqual(d.get_ymax(), 0)
|
||||
self.assertEqual(d.get_yminpos(), 0)
|
||||
|
||||
def test_min_max_helpers_with_data(self):
|
||||
d = Drawable()
|
||||
d.x = np.array([-1.0, 0.0, 2.0, 5.0])
|
||||
d.y = [np.array([1.0, -2.0, 3.0]), np.array([0.5, 4.0, -0.5])]
|
||||
self.assertEqual(d.get_xmin(), -1.0)
|
||||
self.assertEqual(d.get_xmax(), 5.0)
|
||||
self.assertEqual(d.get_xminpos(), 2.0)
|
||||
self.assertEqual(d.get_ymin(), -2.0)
|
||||
self.assertEqual(d.get_ymax(), 4.0)
|
||||
self.assertEqual(d.get_yminpos(), 0.5)
|
||||
|
||||
def test_yminpos_without_positive_values(self):
|
||||
d = Drawable()
|
||||
d.x = np.array([1.0])
|
||||
d.y = [np.array([-1.0, -2.0])]
|
||||
self.assertEqual(d.get_yminpos(), 0)
|
||||
|
||||
def test_set_xdata_set_ydata(self):
|
||||
d = Drawable()
|
||||
d.x = np.zeros(3)
|
||||
d.y = [np.zeros(3)]
|
||||
d.set_xdata(1, 5.0)
|
||||
d.set_ydata(0, 1, 7.0)
|
||||
self.assertEqual(d.get_xdata()[1], 5.0)
|
||||
self.assertEqual(d.get_ydata(0)[1], 7.0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,23 @@
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
|
||||
from damaris.data.Error_Result import Error_Result
|
||||
|
||||
|
||||
class TestErrorResult(unittest.TestCase):
|
||||
def test_plotdata_are_ndarrays(self):
|
||||
"""get_xdata/get_ydata must return numpy arrays like every Drawable."""
|
||||
er = Error_Result("core failed")
|
||||
self.assertIsInstance(er.get_xdata(), np.ndarray)
|
||||
self.assertIsInstance(er.get_ydata(), np.ndarray)
|
||||
np.testing.assert_array_equal(er.get_xdata(), [0.0])
|
||||
np.testing.assert_array_equal(er.get_ydata(), [0.0])
|
||||
|
||||
def test_add_returns_self(self):
|
||||
er = Error_Result("msg")
|
||||
self.assertIs(er + 1, er)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,70 @@
|
||||
import sys
|
||||
import threading
|
||||
import unittest
|
||||
|
||||
from damaris.gui.ExperimentHandling import ExperimentHandling
|
||||
from damaris.gui.ResultHandling import ResultHandling
|
||||
|
||||
|
||||
class _FakeWriter:
|
||||
def __init__(self):
|
||||
self.sent = []
|
||||
|
||||
def send_next(self, job, quit=False):
|
||||
self.sent.append(job)
|
||||
|
||||
|
||||
class _FakeResultIterator:
|
||||
def __init__(self):
|
||||
self.quit_flag = threading.Event()
|
||||
|
||||
def __iter__(self):
|
||||
return iter([])
|
||||
|
||||
|
||||
class TestSandboxStdoutRestore(unittest.TestCase):
|
||||
"""
|
||||
sys.stdout/sys.stderr are process-global: the sandbox redirect in
|
||||
run() must be undone even when the script raises or returns early.
|
||||
"""
|
||||
|
||||
def test_experiment_stdout_restored_after_script_exception(self):
|
||||
orig_stdout = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
handled = ExperimentHandling("raise ValueError('boom')", None, None,
|
||||
log_callback=lambda line: None)
|
||||
handled.run()
|
||||
self.assertIs(sys.stdout, orig_stdout)
|
||||
self.assertIs(sys.stderr, orig_stderr)
|
||||
self.assertIsNotNone(handled.raised_exception)
|
||||
|
||||
def test_experiment_stdout_restored_on_success(self):
|
||||
orig_stdout = sys.stdout
|
||||
writer = _FakeWriter()
|
||||
handled = ExperimentHandling("print('hello')\nexperiment = lambda: iter([])",
|
||||
writer, None, log_callback=lambda line: None)
|
||||
handled.run()
|
||||
self.assertIs(sys.stdout, orig_stdout)
|
||||
self.assertIsNone(handled.raised_exception)
|
||||
|
||||
def test_result_stdout_restored_after_script_exception(self):
|
||||
orig_stdout = sys.stdout
|
||||
orig_stderr = sys.stderr
|
||||
handling = ResultHandling("raise ValueError('boom')", _FakeResultIterator(),
|
||||
None, log_callback=lambda line: None)
|
||||
handling.run()
|
||||
self.assertIs(sys.stdout, orig_stdout)
|
||||
self.assertIs(sys.stderr, orig_stderr)
|
||||
self.assertIsNotNone(handling.raised_exception)
|
||||
|
||||
def test_result_stdout_restored_without_result_function(self):
|
||||
orig_stdout = sys.stdout
|
||||
handling = ResultHandling("x = 1", _FakeResultIterator(), None,
|
||||
log_callback=lambda line: None)
|
||||
handling.run()
|
||||
self.assertIs(sys.stdout, orig_stdout)
|
||||
self.assertIsNone(handling.raised_exception)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,86 @@
|
||||
import io
|
||||
import os
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
import numpy as np
|
||||
import tables
|
||||
|
||||
from damaris.data.MeasurementResult import MeasurementResult, read_from_hdf
|
||||
|
||||
|
||||
class TestMeasurementResult(unittest.TestCase):
|
||||
def test_add_zero_preserves_data(self):
|
||||
"""mr + 0 must return a MeasurementResult with identical data."""
|
||||
mr = MeasurementResult("test_quantity")
|
||||
for i in range(3):
|
||||
mr[i] = float(i * i)
|
||||
mr2 = mr + 0
|
||||
self.assertIsInstance(mr2, MeasurementResult)
|
||||
self.assertEqual(mr2.quantity_name, "test_quantity")
|
||||
np.testing.assert_array_equal(mr2.get_xdata(), mr.get_xdata())
|
||||
np.testing.assert_array_equal(mr2.get_ydata(), mr.get_ydata())
|
||||
|
||||
def test_add_zero_returns_independent_copy(self):
|
||||
"""Changing the copy must not change the original."""
|
||||
mr = MeasurementResult("q")
|
||||
mr[1.0] = 5.0
|
||||
mr2 = mr + 0
|
||||
self.assertIsNot(mr2[1.0], mr[1.0])
|
||||
mr2[1.0] = mr2[1.0] + 100.0
|
||||
self.assertEqual(mr[1.0].mean(), 5.0)
|
||||
|
||||
def test_add_nonzero_raises(self):
|
||||
mr = MeasurementResult("q")
|
||||
self.assertRaises(Exception, mr.__add__, 1)
|
||||
|
||||
def test_get_lineplotdata_returns_means(self):
|
||||
"""get_lineplotdata must return the accumulated means, not self.y."""
|
||||
mr = MeasurementResult("test_quantity")
|
||||
for i in range(3):
|
||||
mr[float(i)] = float(i * i)
|
||||
k, v = mr.get_lineplotdata()
|
||||
self.assertIsInstance(k, np.ndarray)
|
||||
self.assertIsInstance(v, np.ndarray)
|
||||
self.assertEqual(k.shape, v.shape)
|
||||
np.testing.assert_array_equal(v, mr.get_ydata())
|
||||
np.testing.assert_array_equal(v, [0.0, 1.0, 4.0])
|
||||
|
||||
def test_get_lineplotdata_empty(self):
|
||||
"""get_lineplotdata of an empty result must return empty arrays."""
|
||||
mr = MeasurementResult("q")
|
||||
k, v = mr.get_lineplotdata()
|
||||
self.assertEqual(k.shape[0], 0)
|
||||
self.assertEqual(v.shape[0], 0)
|
||||
|
||||
def test_write_to_csv(self):
|
||||
mr = MeasurementResult("q")
|
||||
mr[1.0] = 5.0
|
||||
mr[2.0] = 7.0
|
||||
buf = io.StringIO()
|
||||
mr.write_to_csv(buf)
|
||||
lines = buf.getvalue().splitlines()
|
||||
self.assertEqual(lines[0], "# quantity:q")
|
||||
self.assertEqual(lines[1], "# x y ysigma n")
|
||||
self.assertEqual(len(lines), 4)
|
||||
self.assertEqual(lines[2].split()[3], "1")
|
||||
self.assertEqual(lines[3].split()[3], "1")
|
||||
|
||||
def test_write_to_hdf_roundtrip(self):
|
||||
mr = MeasurementResult("q")
|
||||
mr[1.0] = 5.0
|
||||
mr[2.0] = 7.0
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
path = os.path.join(tmpdir, "test.h5")
|
||||
with tables.open_file(path, mode="w") as h5file:
|
||||
mr.write_to_hdf(h5file, "/", "mr", "test result")
|
||||
with tables.open_file(path, mode="r") as h5file:
|
||||
mr2 = read_from_hdf(h5file.get_node("/mr"))
|
||||
self.assertIsNotNone(mr2)
|
||||
self.assertEqual(mr2.quantity_name, "q")
|
||||
np.testing.assert_array_equal(mr2.get_xdata(), mr.get_xdata())
|
||||
np.testing.assert_array_equal(mr2.get_ydata(), mr.get_ydata())
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user