Skip to content
This repository was archived by the owner on Jul 17, 2024. It is now read-only.

Commit 1643b23

Browse files
Christopher-Chianellitriceo
authored andcommitted
fix: Add tests for new ConstraintVerifier methods
1 parent d454af1 commit 1643b23

File tree

2 files changed

+146
-16
lines changed

2 files changed

+146
-16
lines changed

tests/test_constraint_verifier.py

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
MultiConstraintVerification as JavaMultiConstraintVerification)
1818

1919
def verifier_suite(verifier: ConstraintVerifier, same_value, is_value_one,
20+
EntityValueIndictment, EntityValueJustification, EntityValuePairJustification,
2021
solution, e1, e2, e3, v1, v2, v3):
2122
verifier.verify_that(same_value) \
2223
.given(e1, e2) \
@@ -37,6 +38,11 @@ def verifier_suite(verifier: ConstraintVerifier, same_value, is_value_one,
3738
.given(e1, e2) \
3839
.penalizes(1)
3940

41+
with pytest.raises(AssertionError):
42+
verifier.verify_that(same_value) \
43+
.given(e1, e2) \
44+
.indicts_with_exactly(EntityValueIndictment(e1, v1))
45+
4046
e1.value = v1
4147
e2.value = v1
4248
e3.value = v1
@@ -47,7 +53,47 @@ def verifier_suite(verifier: ConstraintVerifier, same_value, is_value_one,
4753

4854
verifier.verify_that(same_value) \
4955
.given(e1, e2) \
50-
.penalizes()
56+
.penalizes(1)
57+
58+
verifier.verify_that(same_value) \
59+
.given(e1, e2) \
60+
.indicts_with(EntityValueIndictment(e1, e1.value), EntityValueIndictment(e2, v1)) \
61+
.penalizes_by(1)
62+
63+
verifier.verify_that(same_value) \
64+
.given(e1, e2) \
65+
.indicts_with_exactly(EntityValueIndictment(e1, e1.value), EntityValueIndictment(e2, v1)) \
66+
.penalizes_by(1)
67+
68+
verifier.verify_that(same_value) \
69+
.given(e1, e2) \
70+
.indicts_with(EntityValueIndictment(e1, v1))
71+
72+
verifier.verify_that(same_value) \
73+
.given(e1, e2) \
74+
.justifies_with(EntityValuePairJustification((e1, e2), v1, SimpleScore(-1))) \
75+
.penalizes_by(1)
76+
77+
verifier.verify_that(same_value) \
78+
.given(e1, e2) \
79+
.justifies_with_exactly(EntityValuePairJustification((e1, e2), v1, SimpleScore(-1))) \
80+
.penalizes_by(1)
81+
82+
with pytest.raises(AssertionError):
83+
verifier.verify_that(same_value) \
84+
.given(e1, e2) \
85+
.indicts_with_exactly(EntityValueIndictment(e1, v1))
86+
87+
88+
with pytest.raises(AssertionError):
89+
verifier.verify_that(same_value) \
90+
.given(e1, e2) \
91+
.justifies_with(EntityValuePairJustification((e1, e2), v1, SimpleScore(1)))
92+
93+
with pytest.raises(AssertionError):
94+
verifier.verify_that(same_value) \
95+
.given(e1, e2, e3) \
96+
.justifies_with_exactly(EntityValuePairJustification((e1, e2), v1, SimpleScore(1)))
5197

5298
with pytest.raises(AssertionError):
5399
verifier.verify_that(same_value) \
@@ -68,10 +114,28 @@ def verifier_suite(verifier: ConstraintVerifier, same_value, is_value_one,
68114
.given(e1, e2, e3) \
69115
.penalizes(3)
70116

117+
verifier.verify_that(same_value) \
118+
.given(e1, e2, e3) \
119+
.penalizes_more_than(2)
120+
121+
verifier.verify_that(same_value) \
122+
.given(e1, e2, e3) \
123+
.penalizes_less_than(4)
124+
71125
verifier.verify_that(same_value) \
72126
.given(e1, e2, e3) \
73127
.penalizes()
74128

129+
with pytest.raises(AssertionError):
130+
verifier.verify_that(same_value) \
131+
.given(e1, e2, e3) \
132+
.penalizes_more_than(3)
133+
134+
with pytest.raises(AssertionError):
135+
verifier.verify_that(same_value) \
136+
.given(e1, e2, e3) \
137+
.penalizes_less_than(3)
138+
75139
with pytest.raises(AssertionError):
76140
verifier.verify_that(same_value) \
77141
.given(e1, e2, e3) \
@@ -199,28 +263,64 @@ def verifier_suite(verifier: ConstraintVerifier, same_value, is_value_one,
199263

200264

201265
def test_constraint_verifier_create():
202-
@dataclass
266+
@dataclass(unsafe_hash=True)
203267
class Value:
204268
code: str
205269

270+
def __str__(self):
271+
return f'Value({self.code})'
272+
206273
@planning_entity
207-
@dataclass
274+
@dataclass(unsafe_hash=True)
208275
class Entity:
209276
code: str
210-
value: Annotated[Value, PlanningVariable] = field(default=None)
277+
value: Annotated[Value | None, PlanningVariable] = field(default=None)
278+
279+
def __str__(self):
280+
return f'Entity({self.code}, {self.value})'
281+
282+
@dataclass(unsafe_hash=True)
283+
class EntityValueIndictment:
284+
entity: Entity
285+
value: Value
286+
287+
def __str__(self):
288+
return f'EntityValueIndictment({self.entity}, {self.value})'
289+
290+
@dataclass(unsafe_hash=True)
291+
class EntityValueJustification(ConstraintJustification):
292+
entity: Entity
293+
value: Value
294+
score: SimpleScore
295+
296+
def __str__(self):
297+
return f'EntityValueJustification({self.entity}, {self.value}, {self.score})'
298+
299+
@dataclass(unsafe_hash=True)
300+
class EntityValuePairJustification(ConstraintJustification):
301+
entities: tuple[Entity]
302+
value: Value
303+
score: SimpleScore
304+
305+
def __str__(self):
306+
return f'EntityValuePairJustification({self.entities}, {self.value}, {self.score})'
211307

212308
def same_value(constraint_factory: ConstraintFactory):
213309
return (constraint_factory.for_each(Entity)
214310
.join(Entity, Joiners.less_than(lambda e: e.code),
215311
Joiners.equal(lambda e: e.value))
216312
.penalize(SimpleScore.ONE)
313+
.indict_with(lambda e1, e2: [EntityValueIndictment(e1, e1.value), EntityValueIndictment(e2, e2.value)])
314+
.justify_with(lambda e1, e2, score: EntityValuePairJustification((e1, e2), e1.value, score))
217315
.as_constraint('Same Value')
218316
)
219317

220318
def is_value_one(constraint_factory: ConstraintFactory):
221319
return (constraint_factory.for_each(Entity)
222320
.filter(lambda e: e.value.code == 'v1')
223321
.reward(SimpleScore.ONE)
322+
.indict_with(lambda e: [EntityValueIndictment(e, e.value)])
323+
.justify_with(lambda e, score: EntityValueJustification(e, e.value, score))
224324
.as_constraint('Value 1')
225325
)
226326

@@ -259,6 +359,7 @@ class Solution:
259359
solution = Solution([e1, e2, e3], [v1, v2, v3])
260360

261361
verifier_suite(verifier, same_value, is_value_one,
362+
EntityValueIndictment, EntityValueJustification, EntityValuePairJustification,
262363
solution, e1, e2, e3, v1, v2, v3)
263364

264365
verifier = ConstraintVerifier.build(my_constraints, Solution, Entity)
@@ -274,6 +375,7 @@ class Solution:
274375
solution = Solution([e1, e2, e3], [v1, v2, v3])
275376

276377
verifier_suite(verifier, same_value, is_value_one,
378+
EntityValueIndictment, EntityValueJustification, EntityValuePairJustification,
277379
solution, e1, e2, e3, v1, v2, v3)
278380

279381

timefold-solver-python-core/src/main/python/test/__init__.py

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ class SingleConstraintAssertion:
173173
def __init__(self, delegate):
174174
self.delegate = delegate
175175

176-
def justifies_with(self, message: str = None, *justifications: 'ConstraintJustification') \
176+
def justifies_with(self, *justifications: 'ConstraintJustification', message: str = None) \
177177
-> 'SingleConstraintAssertion':
178178
"""
179179
Asserts that the constraint being tested, given a set of facts, results in given justifications.
@@ -192,15 +192,22 @@ def justifies_with(self, message: str = None, *justifications: 'ConstraintJustif
192192
when the expected justifications are not observed
193193
"""
194194
from java.lang import AssertionError as JavaAssertionError # noqa
195+
from _jpyinterpreter import convert_to_java_python_like_object
196+
from java.util import HashMap
197+
reference_map = HashMap()
198+
wrapped_justifications = []
199+
for justification in justifications:
200+
wrapped_justification = convert_to_java_python_like_object(justification, reference_map)
201+
wrapped_justifications.append(wrapped_justification)
195202
try:
196203
if message is None:
197-
return self.delegate.justifiesWith(justifications)
204+
return SingleConstraintAssertion(self.delegate.justifiesWith(*wrapped_justifications))
198205
else:
199-
return self.delegate.justifiesWith(message, justifications)
206+
return SingleConstraintAssertion(self.delegate.justifiesWith(message, *wrapped_justifications))
200207
except JavaAssertionError as e:
201208
raise AssertionError(e.getMessage())
202209

203-
def justifies_with_exactly(self, message: str = None, *justifications: 'ConstraintJustification') \
210+
def justifies_with_exactly(self, *justifications: 'ConstraintJustification', message: str = None) \
204211
-> 'SingleConstraintAssertion':
205212
"""
206213
Asserts that the constraint being tested, given a set of facts, results in given justifications an no others.
@@ -219,15 +226,22 @@ def justifies_with_exactly(self, message: str = None, *justifications: 'Constrai
219226
when the expected justifications are not observed
220227
"""
221228
from java.lang import AssertionError as JavaAssertionError # noqa
229+
from _jpyinterpreter import convert_to_java_python_like_object
230+
from java.util import HashMap
231+
reference_map = HashMap()
232+
wrapped_justifications = []
233+
for justification in justifications:
234+
wrapped_justification = convert_to_java_python_like_object(justification, reference_map)
235+
wrapped_justifications.append(wrapped_justification)
222236
try:
223237
if message is None:
224-
return self.delegate.justifiesWithExactly(justifications)
238+
return SingleConstraintAssertion(self.delegate.justifiesWithExactly(*wrapped_justifications))
225239
else:
226-
return self.delegate.justifiesWithExactly(message, justifications)
240+
return SingleConstraintAssertion(self.delegate.justifiesWithExactly(message, *wrapped_justifications))
227241
except JavaAssertionError as e:
228242
raise AssertionError(e.getMessage())
229243

230-
def indicts_with(self, message: str = None, *indictments) -> 'SingleConstraintAssertion':
244+
def indicts_with(self, *indictments, message: str = None) -> 'SingleConstraintAssertion':
231245
"""
232246
Asserts that the constraint being tested, given a set of facts, results in given indictments.
233247
@@ -245,15 +259,22 @@ def indicts_with(self, message: str = None, *indictments) -> 'SingleConstraintAs
245259
when the expected indictments are not observed
246260
"""
247261
from java.lang import AssertionError as JavaAssertionError # noqa
262+
from _jpyinterpreter import convert_to_java_python_like_object
263+
from java.util import HashMap
264+
reference_map = HashMap()
265+
wrapped_indictments = []
266+
for indictment in indictments:
267+
wrapped_indictment = convert_to_java_python_like_object(indictment, reference_map)
268+
wrapped_indictments.append(wrapped_indictment)
248269
try:
249270
if message is None:
250-
return self.delegate.indictsWith(indictments)
271+
return SingleConstraintAssertion(self.delegate.indictsWith(*wrapped_indictments))
251272
else:
252-
return self.delegate.indictsWith(message, indictments)
273+
return SingleConstraintAssertion(self.delegate.indictsWith(message, *wrapped_indictments))
253274
except JavaAssertionError as e:
254275
raise AssertionError(e.getMessage())
255276

256-
def indicts_with_exactly(self, message: str = None, *indictments) -> 'SingleConstraintAssertion':
277+
def indicts_with_exactly(self, *indictments, message: str = None) -> 'SingleConstraintAssertion':
257278
"""
258279
Asserts that the constraint being tested, given a set of facts, results in given indictments an no others.
259280
@@ -271,11 +292,18 @@ def indicts_with_exactly(self, message: str = None, *indictments) -> 'SingleCons
271292
when the expected indictments are not observed
272293
"""
273294
from java.lang import AssertionError as JavaAssertionError # noqa
295+
from _jpyinterpreter import convert_to_java_python_like_object
296+
from java.util import HashMap
297+
reference_map = HashMap()
298+
wrapped_indictments = []
299+
for indictment in indictments:
300+
wrapped_indictment = convert_to_java_python_like_object(indictment, reference_map)
301+
wrapped_indictments.append(wrapped_indictment)
274302
try:
275303
if message is None:
276-
return self.delegate.indictsWithExactly(indictments)
304+
return SingleConstraintAssertion(self.delegate.indictsWithExactly(*wrapped_indictments))
277305
else:
278-
return self.delegate.indictsWithExactly(message, indictments)
306+
return SingleConstraintAssertion(self.delegate.indictsWithExactly(message, *wrapped_indictments))
279307
except JavaAssertionError as e:
280308
raise AssertionError(e.getMessage())
281309

0 commit comments

Comments
 (0)