1414from typing import ClassVar
1515
1616import numpy as np
17+ import pytest
1718from geoh5py import Workspace
1819from geoh5py .ui_json .annotations import Deprecated
20+ from packaging .version import Version
1921from pydantic import AliasChoices , Field
2022
2123import simpeg_drivers
2931logger = logging .getLogger (__name__ )
3032
3133
32- def test_version_warning (tmp_path , caplog ):
33- workspace = Workspace .create (tmp_path / "test.geoh5" )
34+ def _current_version () -> Version :
35+ """Get the package version."""
36+ return Version (simpeg_drivers .__version__ )
3437
35- with caplog .at_level (logging .WARNING ):
36- _ = SimPEGDriversUIJson (
37- version = "0.2.0" ,
38+
39+ @pytest .fixture (name = "workspace" )
40+ def workspace_fixture (tmp_path ):
41+ """Create a workspace for testing."""
42+ return Workspace .create (tmp_path / "test.geoh5" )
43+
44+
45+ @pytest .fixture (name = "simpeg_uijson_factory" )
46+ def simpeg_uijson_factory_fixture (workspace ):
47+ """Create a SimPEGDriversUIJson object with configurable version."""
48+
49+ def _create_uijson (version : str | None = None , ** kwargs ):
50+ """Create a SimPEGDriversUIJson with the given version and custom fields."""
51+ if version is None :
52+ version = _current_version ().public
53+
54+ return SimPEGDriversUIJson (
55+ version = version ,
3856 title = "My app" ,
3957 icon = "" ,
4058 documentation = "" ,
@@ -43,8 +61,87 @@ def test_version_warning(tmp_path, caplog):
4361 monitoring_directory = "" ,
4462 conda_environment = "my-app" ,
4563 workspace_geoh5 = "" ,
64+ ** kwargs ,
4665 )
4766
67+ return _create_uijson
68+
69+
70+ @pytest .mark .parametrize (
71+ "version_input,expected" ,
72+ [
73+ # Normal version
74+ ("1.2.3" , "1.2.3" ),
75+ # Post-release version
76+ ("1.2.3.post1" , "1.2.3" ),
77+ # RC pre-release version
78+ ("1.2.3rc1" , "1.2.3" ),
79+ # Alpha pre-release version (should not normalize)
80+ ("1.2.3a1" , "1.2.3a1" ),
81+ # Beta pre-release version (should not normalize)
82+ ("1.2.3b1" , "1.2.3b1" ),
83+ # Local version
84+ ("1.2.3+local" , "1.2.3" ),
85+ # Combined cases
86+ ("1.2.3rc1.post2+local" , "1.2.3" ),
87+ ],
88+ )
89+ def test_comparable_version (version_input , expected ):
90+ """Test the comparable_version method of SimPEGDriversUIJson."""
91+ assert SimPEGDriversUIJson .comparable_version (version_input ) == expected
92+
93+
94+ @pytest .mark .parametrize (
95+ "version_input,package_version,should_warn" ,
96+ [
97+ # Different version (should warn)
98+ ("1.0.0" , "2.0.0" , True ),
99+ # Same version (should not warn)
100+ ("2.0.0" , "2.0.0" , False ),
101+ # Post-release variant (should not warn)
102+ ("2.0.0.post1" , "2.0.0" , False ),
103+ ("2.0.0" , "2.0.0.post1" , False ),
104+ # RC variant (should not warn)
105+ ("2.0.0rc1" , "2.0.0" , False ),
106+ ("2.0.0" , "2.0.0rc1" , False ),
107+ ("2.0.0rc1" , "2.0.0rc2" , False ),
108+ # differ by the pre-release number, non RC (should warn)
109+ ("2.0.0a1" , "2.0.0a2" , True ),
110+ ("2.0.0b1" , "2.0.0b2" , True ),
111+ ("2.0.0a1" , "2.0.0" , True ),
112+ ("2.0.0" , "2.0.0a1" , True ),
113+ ("2.0.0a1" , "2.0.0b1" , True ),
114+ ("2.0.0b1" , "2.0.0a1" , True ),
115+ ("2.0.0rc1" , "2.0.0b1" , True ),
116+ ("2.0.0b1" , "2.0.0rc1" , True ),
117+ # same normalized versions (should not warn)
118+ ("2.0.0-beta.1" , "2.0.0b1" , False ),
119+ ("2.0.0b1" , "2.0.0-beta.1" , False ),
120+ ],
121+ )
122+ def test_version_warning (
123+ monkeypatch ,
124+ caplog ,
125+ simpeg_uijson_factory ,
126+ version_input ,
127+ package_version ,
128+ should_warn ,
129+ ):
130+ """Test version warning behavior with mocked package version."""
131+ # Mock the package version
132+ monkeypatch .setattr (simpeg_drivers , "__version__" , package_version )
133+
134+ with caplog .at_level (logging .WARNING ):
135+ caplog .clear ()
136+ _ = simpeg_uijson_factory (version = version_input )
137+
138+ warning_message = f"version '{ version_input } ' does not match the current simpeg-drivers version"
139+ warning_found = any (
140+ warning_message in record .message for record in caplog .records
141+ )
142+
143+ assert warning_found == should_warn
144+
48145
49146def test_write_default (tmp_path ):
50147 default_path = tmp_path / "default.ui.json"
@@ -69,70 +166,34 @@ class MyUIJson(SimPEGDriversUIJson):
69166 with open (default_path , encoding = "utf-8" ) as f :
70167 data = json .load (f )
71168
72- assert data ["version" ] == "0.3.0-alpha.1"
73-
169+ # Use comparable_version for comparison to handle pre/post-release versions
170+ assert SimPEGDriversUIJson .comparable_version (
171+ data ["version" ]
172+ ) == SimPEGDriversUIJson .comparable_version (simpeg_drivers .__version__ )
74173
75- def test_deprecations (tmp_path , caplog ):
76- workspace = Workspace .create (tmp_path / "test.geoh5" )
77174
175+ def test_deprecations (caplog , simpeg_uijson_factory ):
78176 class MyUIJson (SimPEGDriversUIJson ):
79177 my_param : Deprecated
80178
81179 with caplog .at_level (logging .WARNING ):
82- _ = MyUIJson (
83- version = "0.3.0-alpha.1" ,
84- title = "My app" ,
85- icon = "" ,
86- documentation = "" ,
87- geoh5 = str (workspace .h5file ),
88- run_command = "myapp.driver" ,
89- monitoring_directory = "" ,
90- conda_environment = "my-app" ,
91- workspace_geoh5 = "" ,
92- my_param = "whoopsie" ,
93- )
180+ _ = MyUIJson (** simpeg_uijson_factory ().model_dump (), my_param = "whoopsie" )
94181 assert "Skipping deprecated field: my_param." in caplog .text
95182
96183
97- def test_pydantic_deprecation (tmp_path ):
98- workspace = Workspace .create (tmp_path / "test.geoh5" )
99-
184+ def test_pydantic_deprecation (simpeg_uijson_factory ):
100185 class MyUIJson (SimPEGDriversUIJson ):
101186 my_param : str = Field (deprecated = "Use my_param2 instead." , exclude = True )
102187
103- uijson = MyUIJson (
104- version = "0.3.0-alpha.1" ,
105- title = "My app" ,
106- icon = "" ,
107- documentation = "" ,
108- geoh5 = str (workspace .h5file ),
109- run_command = "myapp.driver" ,
110- monitoring_directory = "" ,
111- conda_environment = "my-app" ,
112- workspace_geoh5 = "" ,
113- my_param = "whoopsie" ,
114- )
188+ uijson = MyUIJson (** simpeg_uijson_factory (my_param = "whoopsie" ).model_dump ())
115189 assert "my_param" not in uijson .model_dump ()
116190
117191
118- def test_alias (tmp_path ):
119- workspace = Workspace .create (tmp_path / "test.geoh5" )
120-
192+ def test_alias (simpeg_uijson_factory ):
121193 class MyUIJson (SimPEGDriversUIJson ):
122194 my_param : str = Field (validation_alias = AliasChoices ("my_param" , "myParam" ))
123195
124- uijson = MyUIJson (
125- version = "0.3.0-alpha.1" ,
126- title = "My app" ,
127- icon = "" ,
128- documentation = "" ,
129- geoh5 = str (workspace .h5file ),
130- run_command = "myapp.driver" ,
131- monitoring_directory = "" ,
132- conda_environment = "my-app" ,
133- workspace_geoh5 = "" ,
134- myParam = "hello" ,
135- )
196+ uijson = MyUIJson (** simpeg_uijson_factory (myParam = "hello" ).model_dump ())
136197 assert uijson .my_param == "hello"
137198 assert "myParam" not in uijson .model_fields_set
138199 assert "my_param" in uijson .model_dump ()
@@ -170,7 +231,7 @@ def test_gravity_uijson(tmp_path):
170231 uijson .write (uijson_path )
171232 with open (params_uijson_path , encoding = "utf-8" ) as f :
172233 params_data = json .load (f )
173- assert params_data ["version" ] == simpeg_drivers . __version__
234+ assert Version ( params_data ["version" ]) == Version ( _current_version (). public )
174235 with open (uijson_path , encoding = "utf-8" ) as f :
175236 uijson_data = json .load (f )
176237
0 commit comments