1 {-# LANGUAGE OverloadedLists #-}
2 {-# OPTIONS_GHC -fno-warn-orphans #-}
6 import Test.Tasty.HUnit
8 import Control.Arrow (second)
9 import Data.Hashable (Hashable)
10 import Data.Ratio ((%))
11 import Data.Tree (Tree(..))
12 import GHC.Exts (IsList(..))
14 import qualified Data.HashMap.Strict as HM
22 [ testGroup "MajorityValue" $
24 [(3,15), (2,7), (1,3), (0::Int,2)]
25 [(3,16), (2,6), (1,2), (0,3)]
27 [ let m = mkMerit ['A'..'F'] in
28 testMajorityValueOfMerits
29 [ (The, m [136,307,251,148,84,74])
31 [ (The, [('C',251),('B',307),('D',148),('E',84),('A',136),('F',74)])
33 , let m = mkMerit [ToReject .. TooGood] in
34 testMajorityValueOfMerits
35 [ (This, m [12,10,21,5,5,5,2])
36 , (That, m [12,16,22,3,3,3,1])
38 [ (This, [(Acceptable,21),(Insufficient,10),(Good,5),(ToReject,12),(Perfect,5),(VeryGood,5),(TooGood,2)])
39 , (That, [(Acceptable,22),(Insufficient,16),(ToReject,12),(VeryGood,3),(Perfect,3),(Good,3),(TooGood,1)])
42 , testGroup "MajorityRanking"
43 [ testMajorityValueOfOpinions
44 [ (The, [No,No,No,No,Yes,Yes]) ]
45 [ (The, [(No,4),(Yes,2)]) ]
46 , testMajorityValueOfOpinions
47 [ (The, [No,No,No,Yes,Yes,Yes]) ]
48 [ (The, [(No,3),(Yes,3)]) ]
49 , testMajorityValueOfOpinions
50 [ (This, [No,No,No,No,Yes,Yes])
51 , (That, [No,Yes,Yes,Yes,Yes,Yes])
53 [ (This, [(No,4),(Yes,2)])
54 , (That, [(Yes,5),(No,1)])
56 , testMajorityValueOfOpinions
57 [ (This, [No,No,No,No,No,No])
58 , (That, [No,No,No,Yes,Yes,Yes])
61 , (That, [(No,3),(Yes,3)])
63 , testMajorityValueOfOpinions
64 [ (This, [Yes,Yes,Yes,Yes,Yes,Yes])
65 , (That, [No,No,No,Yes,Yes,Yes])
68 , (That, [(No,3),(Yes,3)])
70 , testMajorityValueOfOpinions
71 [ (This, [No,No,Yes,Yes,Yes,Yes])
72 , (That, [No,No,No,Yes,Yes,Yes])
74 [ (This, [(Yes,4),(No,2)])
75 , (That, [(No,3),(Yes,3)])
77 , testMajorityValueOfOpinions
78 [ (1::Int, [Perfect,Perfect,VeryGood,Perfect,Perfect,Perfect])
79 , (2, [Perfect,VeryGood,VeryGood,VeryGood,Good,VeryGood])
80 , (3, [Acceptable,Perfect,Good,VeryGood,VeryGood,Perfect])
81 , (4, [VeryGood,Good,Acceptable,Good,Good,Good])
82 , (5, [Good,Acceptable,VeryGood,Good,Good,Good])
83 , (6, [VeryGood,Acceptable,Insufficient,Acceptable,Acceptable,Good])
85 [ (1, [(Perfect,5),(VeryGood,1)])
86 , (2, [(VeryGood,4),(Good,1),(Perfect,1)])
87 , (3, [(VeryGood,2),(Good,1),(Perfect,2),(Acceptable,1)])
88 , (4, [(Good,4),(Acceptable,1),(VeryGood,1)])
89 , (5, [(Good,4),(Acceptable,1),(VeryGood,1)])
90 , (6, [(Acceptable,3),(Good,1),(Insufficient,1),(VeryGood,1)])
94 [ testSection "0 judge"
99 , testSection "1 judge, default grade"
103 (Right $ node0 [(This, [(1,[(ToReject,1%1)])])])
104 , testSection "1 judge, default grade, 2 choices"
108 (Right $ node0 [ (This, [(1,[(ToReject,1%1)])])
109 , (That, [(1,[(ToReject,1%1)])])
111 , testSection "1 judge, default grade"
114 (node0 [(This,[(1,Section Nothing Nothing)])])
115 (Right $ node0 [(This,[(1,[(ToReject,1%1)])])])
116 , testSection "2 judges, default grade"
118 [(1::Int,ToReject), (2::Int,ToReject)]
120 [ (This, [(1,Section Nothing Nothing)])
123 [ (This, [ (1,[(ToReject,1%1)])
124 , (2,[(ToReject,1%1)])
127 , testSection "ErrorSection_unknown_choices"
131 (Left $ ErrorSection_unknown_choices [This])
132 , testSection "ErrorSection_unknown_choices"
135 (node0 [(This,[(2,Section Nothing Nothing)])])
136 (Left $ ErrorSection_unknown_choices [This])
137 , testSection "ErrorSection_unknown_choices"
140 (node0 [ (This,[(1,Section Nothing Nothing)])
141 , (That,[(2,Section Nothing Nothing)])
143 (Left $ ErrorSection_unknown_choices [That])
144 , testSection "ErrorSection_unknown_judges"
147 (node0 [(This,[(2,Section Nothing Nothing)])])
148 (Left $ ErrorSection_unknown_judges [(This,[2])])
149 , testSection "1 judge, 1 grade"
152 (node0 [(This,[(1,Section Nothing (Just Acceptable))])])
153 (Right $ node0 [(This,[(1,[(Acceptable,1%1)])])])
154 , testSection "1 judge, 1 grade, 2 sections"
158 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
159 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
160 , node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
163 [ (This, [(1,[(Acceptable,1%1)])]) ]
164 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
165 , node0 [(This, [(1,[(Acceptable,1%1)])])]
167 , testSection "1 judge, 2 grades, 2 sections"
171 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
172 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
173 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
176 [(This, [(1,[(Acceptable,1%2), (Good,1%2)])])]
177 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
178 , node0 [(This, [(1,[(Good,1%1)])])]
180 , testSection "1 judge, 2 grades, 2 sections (1 default)"
184 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
185 [ node0 [(This, [(1,Section Nothing Nothing)])]
186 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
189 [(This, [(1,[(Acceptable,1%2), (Good,1%2)])])]
190 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
191 , node0 [(This, [(1,[(Good,1%1)])])]
193 , testSection "1 judge, 3 grades, 3 sections (2 default)"
197 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
198 [ node0 [(This, [(1,Section Nothing Nothing)])]
199 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
200 , node0 [(This, [(1,Section Nothing (Just VeryGood))])]
203 [(This, [(1,[(Acceptable,1%4), (Good,1%2), (VeryGood,1%4)])])]
204 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
205 , node0 [(This, [(1,[(Good,1%1)])])]
206 , node0 [(This, [(1,[(VeryGood,1%1)])])]
208 , testSection "ErrorSection_invalid_shares sum not 1"
212 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
213 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
214 , node0 [(This, [(1,Section (Just $ 1%3) (Just Good))])]
216 (Left $ ErrorSection_invalid_shares [(This, [(1,[1%2,1%3])])])
217 , testSection "ErrorSection_invalid_shares negative share"
221 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
222 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
223 , node0 [(This, [(1,Section (Just $ -1%2) (Just Good))])]
225 (Left $ ErrorSection_invalid_shares [(This, [(1,[1%2,-1%2])])])
226 , testSection "2 judges, 3 grade, 3 sections (1 default)"
228 [(1::Int,ToReject), (2::Int,ToReject)]
230 [ (This, [(1,Section Nothing (Just Acceptable))])
233 [ (This, [(1,Section Nothing Nothing)])
236 [ (This, [(1,Section (Just $ 1%2) (Just Good))])
240 [ (This, [ (1,[(Acceptable,1%2), (Good,1%2)])
241 , (2,[(ToReject,1%1)])
245 [ (This, [ (1,[(Acceptable,1%1)])
246 , (2,[(ToReject,1%1)])
250 [ (This, [ (1,[(Good,1%1)])
251 , (2,[(ToReject,1%1)])
255 , testSection "2 judges, 4 grades, 5 sections (2 defaults)"
257 [(1::Int,ToReject), (2::Int,ToReject)]
259 [ (This, [(1,Section Nothing (Just Acceptable))])
262 [ (This, [(1,Section Nothing Nothing)])
265 [ (This, [(1,Section (Just $ 1%2) (Just Good))])
268 [ (This, [(1,Section Nothing (Just Good))])
271 [ (This, [ (1,Section Nothing (Just VeryGood))
272 , (2,Section Nothing (Just Insufficient))
278 [ (This, [ (1,[(Acceptable,1%4), (Good,1%2), (VeryGood,1%4)])
279 , (2,[(ToReject,2%3), (Insufficient,1%3)])
283 [ (This, [ (1,[(Acceptable,1%1)])
284 , (2,[(ToReject,1%1)])
288 [ (This, [ (1,[(Good,1%1)])
289 , (2,[(ToReject,1%1)])
293 [ (This, [ (1,[(VeryGood,1%1)])
294 , (2,[(Insufficient,1%1)])
298 [ (This, [ (1,[(VeryGood,1%1)])
299 , (2,[(Insufficient,1%1)])
304 , testSection "1 judge, default grade, 2 choices"
308 (Right $ node0 [ (This,[(1,[(ToReject,1%1)])])
309 , (That,[(1,[(ToReject,1%1)])])
311 , testSection "2 judges, 2 choices"
313 [(1::Int,ToReject), (2::Int,ToReject)]
317 [ (This, [(1,Section Nothing (Just Good))])
318 , (That, [(2,Section Nothing (Just Insufficient))])
321 [ (This, [(1,Section Nothing (Just Acceptable))])
322 , (That, [(2,Section Nothing (Just VeryGood))])
326 [ (This, [ (1,[(Good,1%2), (Acceptable,1%2)])
327 , (2,[(ToReject,1%1)])
329 , (That, [ (1,[(ToReject,1%1)])
330 , (2,[(Insufficient,1%2), (VeryGood,1%2)])
333 [ node0 [ (This, [ (1,[(Good,1%1)])
334 , (2,[(ToReject,1%1)])
336 , (That, [ (1,[(ToReject,1%1)])
337 , (2,[(Insufficient,1%1)])
340 , node0 [ (This, [ (1,[(Acceptable,1%1)])
341 , (2,[(ToReject,1%1)])
343 , (That, [ (1,[(ToReject,1%1)])
344 , (2,[(VeryGood,1%1)])
348 , testSection "1 judge, 1 choice"
352 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
354 , node0 [ (This, [(1,Section Nothing Nothing)])
358 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
360 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
362 , node0 [ (This, [(1,[(ToReject, 1%1)])])
365 , testSection "1 judge, 1 choice (missing judge)"
369 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
375 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
377 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
379 , node0 [ (This, [(1,[(ToReject, 1%1)])])
382 , testSection "1 judge, 1 choice (missing judge)"
388 , node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
392 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
394 [ node0 [ (This, [(1,[(ToReject, 1%1)])])
396 , node0 [ (This, [(1,[(Acceptable, 1%1)])])
399 , testSection "1 judge, 1 choice (missing choice)"
403 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
409 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
411 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
413 , node0 [ (This, [(1,[(ToReject, 1%1)])])
416 , testSection "1 judge, 1 choice (missing choice)"
422 , node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
426 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
428 [ node0 [ (This, [(1,[(ToReject, 1%1)])])
430 , node0 [ (This, [(1,[(Acceptable, 1%1)])])
433 , testSection "2 judges, 2 choices"
435 [(1::Int,ToReject), (2::Int,ToReject)]
437 [ (This, [(1,Section Nothing (Just Acceptable))])
438 , (That, [(2,Section (Just $ 1%8) (Just VeryGood))])
441 [ (This, [ (1,[(Acceptable,1%1)])
442 , (2,[(ToReject,1%1)])
444 , (That, [ (1,[(ToReject,1%1)])
445 , (2,[(VeryGood,1%1)])
448 , testSection "2 judges, 2 choices"
450 [(1::Int,ToReject), (2::Int,ToReject)]
454 [ (This, [(1,Section Nothing (Just Good))])
455 , (That, [(2,Section Nothing (Just Insufficient))])
458 [ (This, [(1,Section Nothing (Just Acceptable))])
459 , (That, [(2,Section (Just $ 1%8) (Just VeryGood))])
463 [ (This, [ (1,[(Good,1%2), (Acceptable,1%2)])
464 , (2,[(ToReject,1%1)])
466 , (That, [ (1,[(ToReject,1%1)])
467 , (2,[(Insufficient,7%8), (VeryGood,1%8)])
470 [ node0 [ (This, [ (1,[(Good,1%1)])
471 , (2,[(ToReject,1%1)])
473 , (That, [ (1,[(ToReject,1%1)])
474 , (2,[(Insufficient,1%1)])
477 , node0 [ (This, [ (1,[(Acceptable,1%1)])
478 , (2,[(ToReject,1%1)])
480 , (That, [ (1,[(ToReject,1%1)])
481 , (2,[(VeryGood,1%1)])
485 , testSection "2 judges, 2 choices"
487 [(1::Int,ToReject), (2::Int,ToReject)]
488 (Node [ (This, [(1,Section Nothing (Just Acceptable))])
490 [ node0 [ (This, [(1,Section Nothing Nothing)])
492 , node0 [ (This, [ (1,Section (Just $ 1%2) (Just Good)) ])
493 , (That, [ (1,Section (Just $ 1%3) Nothing)
494 , (2,Section (Just $ 1%5) (Just Insufficient))
497 , Node [ (This, [(1,Section Nothing (Just Good))])
498 , (That, [(2,Section Nothing (Just VeryGood))])
500 [ node0 [ (This, [ (1,Section Nothing (Just VeryGood))
501 , (2,Section Nothing (Just Insufficient))
503 , (That, [ (1,Section Nothing (Just Acceptable)) ])
505 , node0 [ (This, [ (1,Section Nothing (Just Acceptable))
507 , (That, [ (1,Section Nothing (Just VeryGood))
508 , (2,Section Nothing (Just Good))
514 Node [ (This, [ (1,[(Acceptable,1%4 + 1%8), (Good,1%2), (VeryGood,1%8)])
515 , (2,[(ToReject,1%3 + 1%3 + 1%6), (Insufficient,1%6)])
517 , (That, [ (1,[(ToReject,1%3 + 1%3), (Acceptable,1%6), (VeryGood,1%6)])
518 , (2,[(ToReject,4%10), (Insufficient,1%5), (VeryGood,4%20), (Good,4%20)])
521 [ node0 [ (This, [ (1,[(Acceptable,1%1)]) -- 1%4
522 , (2,[(ToReject,1%1)]) -- 1%3
524 , (That, [ (1,[(ToReject,1%1)]) -- 1%3
525 , (2,[(ToReject,1%1)]) -- 4%10
528 , node0 [ (This, [ (1,[(Good,1%1)]) -- 1%2
529 , (2,[(ToReject,1%1)]) -- 1%3
531 , (That, [ (1,[(ToReject,1%1)]) -- 1%3
532 , (2,[(Insufficient,1%1)]) -- 1%5
535 , Node [ (This, [ (1,[(VeryGood,1%2), (Acceptable,1%2)]) -- 1%4
536 , (2,[(Insufficient,1%2), (ToReject,1%2)]) -- 1%3
538 , (That, [ (1,[(Acceptable,1%2), (VeryGood,1%2)]) -- 1%3
539 , (2,[(VeryGood,1%2), (Good,1%2)]) -- 4%10
542 [ node0 [ (This, [ (1,[(VeryGood,1%1)])
543 , (2,[(Insufficient,1%1)])
545 , (That, [ (1,[(Acceptable,1%1)])
546 , (2,[(VeryGood,1%1)])
549 , node0 [ (This, [ (1,[(Acceptable,1%1)])
550 , (2,[(ToReject,1%1)])
552 , (That, [ (1,[(VeryGood,1%1)])
563 elide :: String -> String
564 elide s | length s > 42 = take 42 s ++ ['…']
567 mkMerit :: (Ord grade, Show grade) => [grade] -> [Share] -> Merit grade
568 mkMerit gs = fromList . (gs`zip`)
571 (Eq choice, Hashable choice, Ord grade) =>
572 [(choice,[grade])] ->
573 MeritByChoice choice grade
575 meritByChoice $ fromList $
576 second (fromList . zip [1::Int ..] . (singleGrade <$>)) <$> os
578 testCompareValue :: (Ord grade, Show grade) =>
579 [(grade, Share)] -> [(grade, Share)] -> TestTree
580 testCompareValue x y =
581 testGroup (elide $ show (x,y))
582 [ testCase "x == x" $ MajorityValue x`compare`MajorityValue x @?= EQ
583 , testCase "y == y" $ MajorityValue y`compare`MajorityValue y @?= EQ
584 , testCase "x < y" $ MajorityValue x`compare`MajorityValue y @?= LT
585 , testCase "y > x" $ MajorityValue y`compare`MajorityValue x @?= GT
588 testMajorityRanking ::
589 (Eq choice, Hashable choice, Ord grade, Show grade, Show choice) =>
590 [(choice, [grade])] ->
591 MajorityRanking choice grade -> TestTree
592 testMajorityRanking os expect =
593 testCase (elide $ show os) $
594 majorityRanking (mkMeritByChoice os) @?= expect
596 testMajorityValueOfOpinions ::
597 (Show grade, Show choice, Ord grade, Eq choice, Hashable choice) =>
598 [(choice, [grade])] ->
599 [(choice, [(grade,Share)])] -> TestTree
600 testMajorityValueOfOpinions os expect =
601 testCase (elide $ show os) $
602 majorityValueByChoice (mkMeritByChoice os)
603 @?= (MajorityValue<$>HM.fromList expect)
605 testMajorityValueOfMerits ::
606 (Show grade, Show choice, Ord grade, Eq choice, Hashable choice) =>
607 MeritByChoice choice grade ->
608 [(choice, [(grade,Share)])] -> TestTree
609 testMajorityValueOfMerits ms expect =
610 testCase (elide $ show ms) $
611 majorityValueByChoice ms
612 @?= (MajorityValue<$>HM.fromList expect)
625 Judges judge grade ->
626 Tree (SectionByJudgeByChoice choice judge grade) ->
627 Either (ErrorSection choice judge grade)
628 (Tree (OpinionsByChoice choice judge grade)) ->
630 testSection msg cs js ss expect =
631 testCase (elide msg) $
632 opinionsBySection cs js ss @?= expect