Skip to content

Commit 18c1a2d

Browse files
committed
new tests for new sensor logic utils
1 parent 7e9c5e9 commit 18c1a2d

File tree

1 file changed

+98
-2
lines changed

1 file changed

+98
-2
lines changed

tests/test_utils.py

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,24 @@
44
import numpy as np
55
import pytest
66
import xarray as xr
7-
from parcels import FieldSet
87

98
import virtualship.utils
9+
from parcels import FieldSet, JITParticle, ScipyParticle, Variable
10+
from virtualship.instruments.sensors import SensorType
1011
from virtualship.instruments.types import InstrumentType
11-
from virtualship.models.expedition import Expedition
12+
from virtualship.models.expedition import Expedition, SensorConfig
1213
from virtualship.models.location import Location
1314
from virtualship.utils import (
1415
PROJECTION,
16+
SENSOR_REGISTRY,
1517
_calc_sail_time,
1618
_calc_wp_stationkeeping_time,
1719
_find_nc_file_with_variable,
1820
_get_bathy_data,
1921
_select_product_id,
2022
_start_end_in_product_timerange,
2123
add_dummy_UV,
24+
build_particle_class_from_sensors,
2225
get_example_expedition,
2326
)
2427

@@ -360,3 +363,96 @@ def test_calc_wp_stationkeeping_time_no_instruments(expedition):
360363

361364
assert stationkeeping_null == stationkeeping_emptylist # are equivalent
362365
assert stationkeeping_null == datetime.timedelta(0) # at least one is 0 time
366+
367+
368+
def test_sensor_registry_every_sensor_type_has_entry():
369+
"""Every SensorType must be present as a key in SENSOR_REGISTRY."""
370+
for sensor in SensorType:
371+
assert sensor in SENSOR_REGISTRY, f"{sensor} missing from SENSOR_REGISTRY"
372+
373+
374+
def test_sensor_registry_no_extra_keys():
375+
"""SENSOR_REGISTRY should not contain keys outside SensorType."""
376+
for key in SENSOR_REGISTRY:
377+
assert isinstance(key, SensorType)
378+
379+
380+
@pytest.mark.parametrize(
381+
"sensor_type",
382+
[
383+
SensorType.OXYGEN,
384+
SensorType.CHLOROPHYLL,
385+
SensorType.NITRATE,
386+
SensorType.PHOSPHATE,
387+
SensorType.PH,
388+
SensorType.PHYTOPLANKTON,
389+
SensorType.PRIMARY_PRODUCTION,
390+
],
391+
)
392+
def test_sensor_registry_bgc_entries_category(sensor_type):
393+
"""All BGC sensors must have category 'bgc'."""
394+
assert SENSOR_REGISTRY[sensor_type].category == "bgc"
395+
396+
397+
def test_sensor_registry_unique_fs_keys():
398+
"""No two sensors should share an fs_key."""
399+
fs_keys = [meta.fs_key for meta in SENSOR_REGISTRY.values()]
400+
assert len(fs_keys) == len(set(fs_keys)), (
401+
"Duplicate fs_key found in SENSOR_REGISTRY"
402+
)
403+
404+
405+
# helper
406+
def _make_sensors(*sensor_types, enabled=True):
407+
"""Helper to build a list of SensorConfig from SensorType values."""
408+
return [SensorConfig(sensor_type=st, enabled=enabled) for st in sensor_types]
409+
410+
411+
def test_build_basic_particle_class():
412+
"""Build basic particle class with T+S sensors and fixed variables."""
413+
fixed = [Variable("cycle_phase", dtype=np.int32, initial=0)]
414+
sensors = _make_sensors(SensorType.TEMPERATURE, SensorType.SALINITY)
415+
416+
ParticleClass = build_particle_class_from_sensors(sensors, fixed, JITParticle)
417+
assert issubclass(ParticleClass, JITParticle)
418+
419+
420+
def test_build_particle_class_disabled_sensors_excluded():
421+
"""Disabled sensors should not contribute variables."""
422+
fixed = []
423+
sensors = [
424+
SensorConfig(sensor_type=SensorType.TEMPERATURE, enabled=True),
425+
SensorConfig(sensor_type=SensorType.SALINITY, enabled=False),
426+
]
427+
428+
ParticleClass = build_particle_class_from_sensors(sensors, fixed, JITParticle)
429+
assert hasattr(ParticleClass, "temperature")
430+
assert not hasattr(ParticleClass, "salinity")
431+
432+
433+
def test_build_particle_class_empty_sensors():
434+
"""With no sensors, build_particle_class_from_sensors returns a class with only fixed variables."""
435+
fixed = [Variable("raising", dtype=np.int8, initial=0)]
436+
sensors = []
437+
438+
ParticleClass = build_particle_class_from_sensors(sensors, fixed, JITParticle)
439+
assert hasattr(ParticleClass, "raising")
440+
441+
442+
def test_build_particle_class_velocity_adds_U_V():
443+
"""VELOCITY sensor should add both U and V particle variables."""
444+
fixed = []
445+
sensors = _make_sensors(SensorType.VELOCITY)
446+
447+
ParticleClass = build_particle_class_from_sensors(sensors, fixed, JITParticle)
448+
assert hasattr(ParticleClass, "U")
449+
assert hasattr(ParticleClass, "V")
450+
451+
452+
def test_build_particle_class_scipy_base():
453+
"""Should also work with ScipyParticle as the base class."""
454+
fixed = []
455+
sensors = _make_sensors(SensorType.TEMPERATURE)
456+
457+
ParticleClass = build_particle_class_from_sensors(sensors, fixed, ScipyParticle)
458+
assert issubclass(ParticleClass, ScipyParticle)

0 commit comments

Comments
 (0)