Skip to content

Commit 2ee580e

Browse files
committed
test: add tests for app_runmacro.py
1 parent feb7259 commit 2ee580e

3 files changed

Lines changed: 178 additions & 11 deletions

File tree

news/add-test.rst

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
**Added:**
2+
3+
* No news added: Add more tests fro app_runmacro.py.
4+
5+
**Changed:**
6+
7+
* <news item>
8+
9+
**Deprecated:**
10+
11+
* <news item>
12+
13+
**Removed:**
14+
15+
* <news item>
16+
17+
**Fixed:**
18+
19+
* <news item>
20+
21+
**Security:**
22+
23+
* <news item>

src/diffpy/apps/app_runmacro.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
grammar = r"""
1111
Program:
1212
commands*=Command
13-
variable=VariableBlock
13+
variable=VariableBlock?
1414
;
1515
1616
Command:
@@ -86,9 +86,22 @@ def input_as_list(self, key, value):
8686
def load_command_processor(self, command):
8787
if command.component == "structure":
8888
# TODO: support multiple structures input in the future
89+
if (
90+
self.inputs.get("initialize_structures.structure_paths")
91+
is not None
92+
):
93+
raise ValueError(
94+
"Multiple structures are not supported by `runmacro` yet. "
95+
"Please use python script instead."
96+
)
8997
key = "initialize_structures.structure_paths"
9098
variable = "structure"
9199
elif command.component == "profile":
100+
if self.inputs.get("initialize_profile.profile_path") is not None:
101+
raise ValueError(
102+
"Multiple profiles are not supported by `runmacro`. "
103+
"Please use python script instead."
104+
)
92105
key = "initialize_profile.profile_path"
93106
variable = "profile"
94107
else:

tests/test_runmacro.py

Lines changed: 141 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
from pathlib import Path
22

33
import numpy
4+
import pytest
45
from helper import make_cmi_recipe
56
from scipy.optimize import least_squares
67

78
from diffpy.apps.app_runmacro import MacroParser
89

10+
_STRUCTURE_PATH = str(Path(__file__).parent / "data" / "Ni.cif")
11+
_PROFILE_PATH = str(Path(__file__).parent / "data" / "Ni.gr")
12+
913

1014
def test_meta_model():
1115
# C1: Run the same fit with pdfadapter and diffpy_cmi
1216
# Expect the refined parameters to be the same within 1e-5
1317
# diffpy_cmi fitting
14-
structure_path = Path(__file__).parent / "data" / "Ni.cif"
15-
profile_path = Path(__file__).parent / "data" / "Ni.gr"
1618
initial_pv_dict = {
1719
"s0": 0.4,
1820
"qdamp": 0.04,
@@ -30,12 +32,7 @@ def test_meta_model():
3032
"qbroad",
3133
]
3234
diffpycmi_recipe = make_cmi_recipe(
33-
str(structure_path), str(profile_path), initial_pv_dict
34-
)
35-
structure_path = Path(__file__).parent / "data" / "Ni.cif"
36-
profile_path = Path(__file__).parent / "data" / "Ni.gr"
37-
diffpycmi_recipe = make_cmi_recipe(
38-
str(structure_path), str(profile_path), initial_pv_dict
35+
_STRUCTURE_PATH, _PROFILE_PATH, initial_pv_dict
3936
)
4037
diffpycmi_recipe.fithooks[0].verbose = 0
4138
diffpycmi_recipe.fix("all")
@@ -51,8 +48,8 @@ def test_meta_model():
5148
diffpy_pv_dict[pname] = parameter.value
5249

5350
diffpy_dsl = f"""
54-
load structure G1 from "{str(structure_path)}"
55-
load profile exp_ni from "{str(profile_path)}"
51+
load structure G1 from "{_STRUCTURE_PATH}"
52+
load profile exp_ni from "{_PROFILE_PATH}"
5653
5754
set G1 spacegroup as auto
5855
set exp_ni q_range as 0.1 25
@@ -78,3 +75,137 @@ def test_meta_model():
7875
diffpy_value = diffpy_pv_dict[var_name]
7976
interpreter_value = interpreter_results["variables"][var_name]["value"]
8077
assert numpy.isclose(diffpy_value, interpreter_value, atol=1e-5)
78+
79+
80+
@pytest.mark.parametrize(
81+
"command_string, expected_inputs, expected_variables",
82+
[
83+
# C1: load structure G1 from "path/to/structure.cif"
84+
# Expect inputs and variables to be set correctly
85+
(
86+
f'load structure G1 from "{_STRUCTURE_PATH}"',
87+
{
88+
"initialize_structures.structure_paths": _STRUCTURE_PATH,
89+
"initialize_structures.names": ["G1"],
90+
},
91+
{"G1": "structure"},
92+
),
93+
# C2: load profile exp_ni from "path/to/profile.gr"
94+
# Expect inputs and variables to be set correctly
95+
(
96+
f'load profile exp_ni from "{_PROFILE_PATH}"',
97+
{"initialize_profile.profile_path": _PROFILE_PATH},
98+
{"exp_ni": "profile"},
99+
),
100+
# C3: create equation variables s0
101+
# Expect variable names to be stored
102+
(
103+
"create equation variables s0",
104+
{"add_contribution_variables.variable_names": ["s0"]},
105+
{},
106+
),
107+
# C4: save to "results.json"
108+
# Expect result path to be stored
109+
(
110+
'save to "results.json"',
111+
{"save_results.result_path": "results.json"},
112+
{},
113+
),
114+
# C5: set equation as "s0*G1"
115+
# Expect equation to be stored
116+
(
117+
'set equation as "s0*G1"',
118+
{"initialize_contribution.equation": ["s0*G1"]},
119+
{},
120+
),
121+
# C6: set exp_ni q_range as 0.1 25
122+
# Expect q_range to be stored
123+
(
124+
f'load profile exp_ni from "{_PROFILE_PATH}"\n'
125+
"set exp_ni q_range as 0.1 25",
126+
{"initialize_profile.q_range": [0.1, 25]},
127+
{"exp_ni": "profile"},
128+
),
129+
# C7: set exp_ni calculation_range as 1.5 50 0.01
130+
# Expect calculation_range to be stored
131+
(
132+
f'load profile exp_ni from "{_PROFILE_PATH}"\n'
133+
"set exp_ni calculation_range as 1.5 50 0.01",
134+
{"initialize_profile.calculation_range": [1.5, 50, 0.01]},
135+
{"exp_ni": "profile"},
136+
),
137+
# C8: set G1 spacegroup as auto
138+
# Expect spacegroup to be stored
139+
(
140+
f'load structure G1 from "{_STRUCTURE_PATH}"\n'
141+
"set G1 spacegroup as auto",
142+
{"initialize_structures.spacegroups": ["auto"]},
143+
{"G1": "structure"},
144+
),
145+
# C9: variables section with multiple variables
146+
# Expect variable names and values to be stored
147+
(
148+
"""
149+
variables:
150+
---
151+
- G1_a: 3.52
152+
- s0
153+
- G1_Uiso_0: 0.005
154+
---
155+
""",
156+
{
157+
"set_initial_variable_values.variable_name_to_value": {
158+
"G1_a": 3.52,
159+
"G1_Uiso_0": 0.005,
160+
},
161+
"refine_variables.variable_names": ["G1_a", "s0", "G1_Uiso_0"],
162+
},
163+
{},
164+
),
165+
],
166+
)
167+
def test_command_processor(
168+
command_string, expected_inputs, expected_variables
169+
):
170+
parser = MacroParser()
171+
parser.parse(command_string)
172+
for key, value in expected_inputs.items():
173+
assert parser.inputs[key] == value
174+
assert dict(parser.variables) == expected_variables
175+
176+
177+
@pytest.mark.parametrize(
178+
"command_string, expected_exception, match",
179+
[
180+
(
181+
f'load unknown foo from "{_STRUCTURE_PATH}"',
182+
ValueError,
183+
"Unknown component type: unknown "
184+
"Please use 'structure' or 'profile'.",
185+
),
186+
(
187+
'load structure foo from "/nonexistent/path.cif"',
188+
FileNotFoundError,
189+
"structure /nonexistent/path.cif not found. "
190+
"Please ensure the path is correct and the file exists.",
191+
),
192+
(
193+
f'load structure G1 from "{_STRUCTURE_PATH}"\n'
194+
f'load structure G2 from "{_STRUCTURE_PATH}"',
195+
ValueError,
196+
"Multiple structures are not supported by `runmacro` yet. "
197+
"Please use python script instead.",
198+
),
199+
(
200+
f'load profile p1 from "{_PROFILE_PATH}"\n'
201+
f'load profile p2 from "{_PROFILE_PATH}"',
202+
ValueError,
203+
"Multiple profiles are not supported by `runmacro`. "
204+
"Please use python script instead.",
205+
),
206+
],
207+
)
208+
def test_load_command_processor_bad(command_string, expected_exception, match):
209+
parser = MacroParser()
210+
with pytest.raises(expected_exception, match=match):
211+
parser.parse(command_string)

0 commit comments

Comments
 (0)