11package ai .timefold .solver .core .api .score .analysis ;
22
33import static ai .timefold .solver .core .impl .score .director .InnerScoreDirector .getConstraintAnalysis ;
4+ import static org .assertj .core .api .AssertionsForClassTypes .assertThat ;
5+ import static org .assertj .core .api .AssertionsForClassTypes .assertThatThrownBy ;
46import static org .assertj .core .api .SoftAssertions .assertSoftly ;
57
68import java .util .Arrays ;
@@ -26,6 +28,131 @@ void empty() {
2628 softly .assertThat (diff .score ()).isEqualTo (SimpleScore .of (0 ));
2729 softly .assertThat (diff .constraintMap ()).isEmpty ();
2830 });
31+
32+ var summary = scoreAnalysis .summarize ();
33+ assertThat (summary )
34+ .isEqualTo ("""
35+ Explanation of score (0):
36+ Constraint matches:
37+ """ );
38+ }
39+
40+ @ Test
41+ void summarize () {
42+ var constraintPackage = "constraintPackage" ;
43+ var constraintName1 = "constraint1" ;
44+ var constraintName2 = "constraint2" ;
45+ var constraintName3 = "constraint3" ;
46+ var constraintId1 = ConstraintRef .of (constraintPackage , constraintName1 );
47+ var constraintId2 = ConstraintRef .of (constraintPackage , constraintName2 );
48+ var constraintId3 = ConstraintRef .of (constraintPackage , constraintName3 );
49+
50+ var constraintMatchTotal = new DefaultConstraintMatchTotal <>(constraintId1 , SimpleScore .of (1 ));
51+ addConstraintMatch (constraintMatchTotal , SimpleScore .of (2 ), "A" , "B" , "C" );
52+ addConstraintMatch (constraintMatchTotal , SimpleScore .of (4 ), "A" , "B" );
53+ addConstraintMatch (constraintMatchTotal , SimpleScore .of (6 ), "B" , "C" );
54+ addConstraintMatch (constraintMatchTotal , SimpleScore .of (7 ), "C" );
55+ addConstraintMatch (constraintMatchTotal , SimpleScore .of (8 ));
56+ var constraintMatchTotal2 = new DefaultConstraintMatchTotal <>(constraintId2 , SimpleScore .of (3 ));
57+ addConstraintMatch (constraintMatchTotal2 , SimpleScore .of (3 ), "B" , "C" , "D" );
58+ addConstraintMatch (constraintMatchTotal2 , SimpleScore .of (6 ), "B" , "C" );
59+ addConstraintMatch (constraintMatchTotal2 , SimpleScore .of (9 ), "C" , "D" );
60+ addConstraintMatch (constraintMatchTotal2 , SimpleScore .of (10 ), "D" );
61+ addConstraintMatch (constraintMatchTotal2 , SimpleScore .of (12 ));
62+ var emptyConstraintMatchTotal1 = new DefaultConstraintMatchTotal <>(constraintId3 , SimpleScore .of (0 ));
63+ var constraintAnalysisMap = Map .of (
64+ constraintMatchTotal .getConstraintRef (), getConstraintAnalysis (constraintMatchTotal , true ),
65+ constraintMatchTotal2 .getConstraintRef (), getConstraintAnalysis (constraintMatchTotal2 , true ),
66+ emptyConstraintMatchTotal1 .getConstraintRef (), getConstraintAnalysis (emptyConstraintMatchTotal1 , true ));
67+ var scoreAnalysis = new ScoreAnalysis <>(SimpleScore .of (67 ), constraintAnalysisMap );
68+
69+ // Single constraint analysis
70+ var constraintSummary = constraintAnalysisMap .get (constraintMatchTotal .getConstraintRef ()).summarize ();
71+ assertThat (constraintSummary )
72+ .isEqualTo ("""
73+ Explanation of score (27):
74+ Constraint matches:
75+ 27: constraint (constraint1) has 5 matches:
76+ 2: justified with ([A, B, C])
77+ 4: justified with ([A, B])
78+ 6: justified with ([B, C])
79+ ...
80+ """ );
81+
82+ // Complete score analysis
83+ var summary = scoreAnalysis .summarize ();
84+ assertThat (scoreAnalysis .getConstraintAnalysis (constraintPackage , constraintName1 ).matchCount ()).isEqualTo (5 );
85+ assertThat (summary )
86+ .isEqualTo ("""
87+ Explanation of score (67):
88+ Constraint matches:
89+ 0: constraint (constraint3) has no matches.
90+ 27: constraint (constraint1) has 5 matches:
91+ 2: justified with ([A, B, C])
92+ 4: justified with ([A, B])
93+ 6: justified with ([B, C])
94+ ...
95+ 40: constraint (constraint2) has 5 matches:
96+ 3: justified with ([B, C, D])
97+ 6: justified with ([B, C])
98+ 9: justified with ([C, D])
99+ ...
100+ """ );
101+ }
102+
103+ @ Test
104+ void summarizeUninitializedSolution () {
105+ var constraintPackage = "constraintPackage" ;
106+ var constraintName1 = "constraint1" ;
107+ var constraintName2 = "constraint2" ;
108+ var constraintId1 = ConstraintRef .of (constraintPackage , constraintName1 );
109+ var constraintId2 = ConstraintRef .of (constraintPackage , constraintName2 );
110+
111+ var constraintMatchTotal = new DefaultConstraintMatchTotal <>(constraintId1 , SimpleScore .of (0 ));
112+ var constraintMatchTotal2 = new DefaultConstraintMatchTotal <>(constraintId2 , SimpleScore .of (0 ));
113+ var constraintAnalysisMap = Map .of (
114+ constraintMatchTotal .getConstraintRef (), getConstraintAnalysis (constraintMatchTotal , true ),
115+ constraintMatchTotal2 .getConstraintRef (), getConstraintAnalysis (constraintMatchTotal2 , true ));
116+ var scoreAnalysis = new ScoreAnalysis <>(SimpleScore .ofUninitialized (3 , 0 ), constraintAnalysisMap );
117+
118+ // Single constraint analysis
119+ var constraintSummary = constraintAnalysisMap .get (constraintMatchTotal .getConstraintRef ()).summarize ();
120+ assertThat (constraintSummary )
121+ .isEqualTo ("""
122+ Explanation of score (0):
123+ Constraint matches:
124+ 0: constraint (constraint1) has no matches.
125+ """ );
126+
127+ // Complete score analysis
128+ var summary = scoreAnalysis .summarize ();
129+ assertThat (scoreAnalysis .getConstraintAnalysis (constraintPackage , constraintName1 ).matchCount ()).isZero ();
130+ assertThat (summary )
131+ .isEqualTo ("""
132+ Explanation of score (3init/0):
133+ Constraint matches:
134+ 0: constraint (constraint1) has no matches.
135+ 0: constraint (constraint2) has no matches.
136+ """ );
137+ }
138+
139+ @ Test
140+ void failFastSummarize () {
141+ var constraintPackage = "constraintPackage" ;
142+ var constraintName1 = "constraint1" ;
143+ var constraintId1 = ConstraintRef .of (constraintPackage , constraintName1 );
144+
145+ var constraintMatchTotal = new DefaultConstraintMatchTotal <>(constraintId1 , SimpleScore .of (1 ));
146+ addConstraintMatch (constraintMatchTotal , SimpleScore .of (2 ), "A" , "B" , "C" );
147+ var constraintAnalysisMap = Map .of (
148+ constraintMatchTotal .getConstraintRef (), getConstraintAnalysis (constraintMatchTotal , false ));
149+ var scoreAnalysis = new ScoreAnalysis <>(SimpleScore .of (3 ), constraintAnalysisMap );
150+
151+ assertThatThrownBy (scoreAnalysis ::summarize )
152+ .hasMessageContaining ("The constraint matches must be non-null" );
153+
154+ assertThatThrownBy (() -> constraintAnalysisMap .values ().stream ().findFirst ().get ().matchCount ())
155+ .hasMessageContaining ("The constraint matches must be non-null" );
29156 }
30157
31158 @ Test
0 commit comments