|
12 | 12 | import ai.timefold.solver.core.api.score.stream.ConstraintProvider; |
13 | 13 | import ai.timefold.solver.core.config.solver.EnvironmentMode; |
14 | 14 | import ai.timefold.solver.core.impl.domain.solution.descriptor.SolutionDescriptor; |
15 | | - |
| 15 | +import ai.timefold.solver.test.api.score.stream.ConstraintVerifier; |
| 16 | + |
| 17 | +/** |
| 18 | + * Represents a {@link ConstraintVerifier} with pre-set values. |
| 19 | + * A new instance of this class will be created should this change. |
| 20 | + * <p> |
| 21 | + * This class still needs to be thread-safe, as {@link ScoreDirectorFactoryCache} is not. |
| 22 | + * Failure to do so will lead to intermittent failures in tests when run in parallel. |
| 23 | + * |
| 24 | + * @param <ConstraintProvider_> |
| 25 | + * @param <Solution_> |
| 26 | + * @param <Score_> |
| 27 | + */ |
16 | 28 | final class ConfiguredConstraintVerifier<ConstraintProvider_ extends ConstraintProvider, Solution_, Score_ extends Score<Score_>> { |
17 | 29 |
|
18 | | - /** |
19 | | - * Exists so that people can not, even by accident, pick the same constraint ID as the default cache key. |
20 | | - */ |
| 30 | + // Exists so that people can not, even by accident, pick the same constraint ID as the default cache key. |
21 | 31 | private final ConstraintRef defaultScoreDirectorFactoryMapKey = |
22 | 32 | ConstraintRef.of(UUID.randomUUID().toString(), UUID.randomUUID().toString()); |
23 | 33 |
|
24 | 34 | private final ConstraintProvider_ constraintProvider; |
25 | | - private final ScoreDirectorFactoryCache<ConstraintProvider_, Solution_, Score_> scoreDirectorFactoryContainer; |
| 35 | + @SuppressWarnings("java:S5164") // Suppress SonarCloud ThreadLocal warning; this is safe in the context of tests. |
| 36 | + private final ThreadLocal<ScoreDirectorFactoryCache<ConstraintProvider_, Solution_, Score_>> scoreDirectorFactoryContainerThreadLocal; |
26 | 37 |
|
27 | 38 | public ConfiguredConstraintVerifier(ConstraintProvider_ constraintProvider, |
28 | 39 | SolutionDescriptor<Solution_> solutionDescriptor) { |
29 | 40 | this.constraintProvider = constraintProvider; |
30 | | - this.scoreDirectorFactoryContainer = new ScoreDirectorFactoryCache<>(solutionDescriptor); |
| 41 | + this.scoreDirectorFactoryContainerThreadLocal = |
| 42 | + ThreadLocal.withInitial(() -> new ScoreDirectorFactoryCache<>(solutionDescriptor)); |
31 | 43 | } |
32 | 44 |
|
33 | 45 | public DefaultSingleConstraintVerification<Solution_, Score_> verifyThat( |
34 | 46 | BiFunction<ConstraintProvider_, ConstraintFactory, Constraint> constraintFunction) { |
35 | 47 | requireNonNull(constraintFunction); |
36 | | - var scoreDirectorFactory = scoreDirectorFactoryContainer.getScoreDirectorFactory(constraintFunction, constraintProvider, |
37 | | - EnvironmentMode.FULL_ASSERT); |
| 48 | + var scoreDirectorFactory = scoreDirectorFactoryContainerThreadLocal.get().getScoreDirectorFactory(constraintFunction, |
| 49 | + constraintProvider, EnvironmentMode.FULL_ASSERT); |
38 | 50 | return new DefaultSingleConstraintVerification<>(scoreDirectorFactory); |
39 | 51 | } |
40 | 52 |
|
41 | 53 | public DefaultMultiConstraintVerification<Solution_, Score_> verifyThat() { |
42 | | - var scoreDirectorFactory = scoreDirectorFactoryContainer.getScoreDirectorFactory(defaultScoreDirectorFactoryMapKey, |
43 | | - constraintProvider, EnvironmentMode.FULL_ASSERT); |
| 54 | + var scoreDirectorFactory = scoreDirectorFactoryContainerThreadLocal.get() |
| 55 | + .getScoreDirectorFactory(defaultScoreDirectorFactoryMapKey, constraintProvider, EnvironmentMode.FULL_ASSERT); |
44 | 56 | return new DefaultMultiConstraintVerification<>(scoreDirectorFactory, constraintProvider); |
45 | 57 | } |
46 | 58 |
|
|
0 commit comments