1 {-# LANGUAGE OverloadedLists #-}
2 {-# LANGUAGE TypeFamilies #-}
3 {-# OPTIONS_GHC -fno-warn-orphans #-}
7 import Test.Tasty.HUnit
9 import Control.Arrow (second)
10 import Data.Hashable (Hashable)
11 import Data.Ratio ((%))
12 import Data.Tree (Tree(..))
13 import GHC.Exts (IsList(..))
15 import qualified Data.HashMap.Strict as HM
23 [ testGroup "MajorityValue" $
25 [(3,15), (2,7), (1,3), (0::Int,2)]
26 [(3,16), (2,6), (1,2), (0,3)]
28 [ let m = mkMerit ['A'..'F'] in
29 testMajorityValueOfMerits
30 [ (The, m [136,307,251,148,84,74])
32 [ (The, [('C',251),('B',307),('D',148),('E',84),('A',136),('F',74)])
34 , let m = mkMerit [ToReject .. TooGood] in
35 testMajorityValueOfMerits
36 [ (This, m [12,10,21,5,5,5,2])
37 , (That, m [12,16,22,3,3,3,1])
39 [ (This, [(Acceptable,21),(Insufficient,10),(Good,5),(ToReject,12),(Perfect,5),(VeryGood,5),(TooGood,2)])
40 , (That, [(Acceptable,22),(Insufficient,16),(ToReject,12),(VeryGood,3),(Perfect,3),(Good,3),(TooGood,1)])
43 , testGroup "MajorityRanking"
44 [ testMajorityValueOfOpinions
45 [ (The, [No,No,No,No,Yes,Yes]) ]
46 [ (The, [(No,4),(Yes,2)]) ]
47 , testMajorityValueOfOpinions
48 [ (The, [No,No,No,Yes,Yes,Yes]) ]
49 [ (The, [(No,3),(Yes,3)]) ]
50 , testMajorityValueOfOpinions
51 [ (This, [No,No,No,No,Yes,Yes])
52 , (That, [No,Yes,Yes,Yes,Yes,Yes])
54 [ (This, [(No,4),(Yes,2)])
55 , (That, [(Yes,5),(No,1)])
57 , testMajorityValueOfOpinions
58 [ (This, [No,No,No,No,No,No])
59 , (That, [No,No,No,Yes,Yes,Yes])
62 , (That, [(No,3),(Yes,3)])
64 , testMajorityValueOfOpinions
65 [ (This, [Yes,Yes,Yes,Yes,Yes,Yes])
66 , (That, [No,No,No,Yes,Yes,Yes])
69 , (That, [(No,3),(Yes,3)])
71 , testMajorityValueOfOpinions
72 [ (This, [No,No,Yes,Yes,Yes,Yes])
73 , (That, [No,No,No,Yes,Yes,Yes])
75 [ (This, [(Yes,4),(No,2)])
76 , (That, [(No,3),(Yes,3)])
78 , testMajorityValueOfOpinions
79 [ (1::Int, [Perfect,Perfect,VeryGood,Perfect,Perfect,Perfect])
80 , (2, [Perfect,VeryGood,VeryGood,VeryGood,Good,VeryGood])
81 , (3, [Acceptable,Perfect,Good,VeryGood,VeryGood,Perfect])
82 , (4, [VeryGood,Good,Acceptable,Good,Good,Good])
83 , (5, [Good,Acceptable,VeryGood,Good,Good,Good])
84 , (6, [VeryGood,Acceptable,Insufficient,Acceptable,Acceptable,Good])
86 [ (1, [(Perfect,5),(VeryGood,1)])
87 , (2, [(VeryGood,4),(Good,1),(Perfect,1)])
88 , (3, [(VeryGood,2),(Good,1),(Perfect,2),(Acceptable,1)])
89 , (4, [(Good,4),(Acceptable,1),(VeryGood,1)])
90 , (5, [(Good,4),(Acceptable,1),(VeryGood,1)])
91 , (6, [(Acceptable,3),(Good,1),(Insufficient,1),(VeryGood,1)])
95 [ testSection "0 judge"
100 , testSection "1 judge, default grade"
104 (Right $ node0 [(This, [(1,[(ToReject,1%1)])])])
105 , testSection "1 judge, default grade, 2 choices"
109 (Right $ node0 [ (This, [(1,[(ToReject,1%1)])])
110 , (That, [(1,[(ToReject,1%1)])])
112 , testSection "1 judge, default grade"
115 (node0 [(This,[(1,Section Nothing Nothing)])])
116 (Right $ node0 [(This,[(1,[(ToReject,1%1)])])])
117 , testSection "2 judges, default grade"
119 [(1::Int,ToReject), (2::Int,ToReject)]
121 [ (This, [(1,Section Nothing Nothing)])
124 [ (This, [ (1,[(ToReject,1%1)])
125 , (2,[(ToReject,1%1)])
128 , testSection "ErrorSection_unknown_choices"
132 (Left $ ErrorSection_unknown_choices [This])
133 , testSection "ErrorSection_unknown_choices"
136 (node0 [(This,[(2,Section Nothing Nothing)])])
137 (Left $ ErrorSection_unknown_choices [This])
138 , testSection "ErrorSection_unknown_choices"
141 (node0 [ (This,[(1,Section Nothing Nothing)])
142 , (That,[(2,Section Nothing Nothing)])
144 (Left $ ErrorSection_unknown_choices [That])
145 , testSection "ErrorSection_unknown_judges"
148 (node0 [(This,[(2,Section Nothing Nothing)])])
149 (Left $ ErrorSection_unknown_judges [(This,[2])])
150 , testSection "1 judge, 1 grade"
153 (node0 [(This,[(1,Section Nothing (Just Acceptable))])])
154 (Right $ node0 [(This,[(1,[(Acceptable,1%1)])])])
155 , testSection "1 judge, 1 grade, 2 sections"
159 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
160 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
161 , node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
164 [ (This, [(1,[(Acceptable,1%1)])]) ]
165 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
166 , node0 [(This, [(1,[(Acceptable,1%1)])])]
168 , testSection "sectionNodeShare with judge"
170 [(1::Int,ToReject), (2,Insufficient)]
172 [(This, [(1,Section Nothing (Just Acceptable))])]
173 [ node0 $ SectionNode (Just $ 1%3) [(This, [ (1,Section (Just $ 1%2) Nothing)
174 , (2,Section Nothing Nothing)
176 , node0 [(This, [ (1,Section (Just $ 1%2) Nothing)
177 , (2,Section Nothing (Just Good))
181 [ (This, [ (1,[(Acceptable,1%2 + 1%2)])
182 , (2,[(Insufficient,1%3), (Good,2%3)])
184 [ node0 [(This, [ (1,[(Acceptable,1%1)])
185 , (2,[(Insufficient,1%1)])
187 , node0 [(This, [ (1,[(Acceptable,1%1)])
191 , testSection "sectionNodeShare without judge"
193 [(1::Int,ToReject), (2,Insufficient)]
195 [(This, [(1,Section Nothing (Just Acceptable))])]
196 [ node0 $ SectionNode (Just $ 1%3) [(This, [ (1,Section (Just $ 1%2) Nothing) ])]
197 , node0 [(This, [ (1,Section (Just $ 1%2) Nothing)
198 , (2,Section Nothing (Just Good))
202 [ (This, [ (1,[(Acceptable,1%2 + 1%2)])
203 , (2,[(Insufficient,1%3), (Good,2%3)])
205 [ node0 [(This, [ (1,[(Acceptable,1%1)])
206 , (2,[(Insufficient,1%1)])
208 , node0 [(This, [ (1,[(Acceptable,1%1)])
212 , testSection "1 judge, 2 grades, 2 sections"
216 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
217 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
218 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
221 [(This, [(1,[(Acceptable,1%2), (Good,1%2)])])]
222 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
223 , node0 [(This, [(1,[(Good,1%1)])])]
225 , testSection "1 judge, 2 grades, 2 sections (1 default)"
229 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
230 [ node0 [(This, [(1,Section Nothing Nothing)])]
231 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
234 [(This, [(1,[(Acceptable,1%2), (Good,1%2)])])]
235 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
236 , node0 [(This, [(1,[(Good,1%1)])])]
238 , testSection "1 judge, 3 grades, 3 sections (2 default)"
242 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
243 [ node0 [(This, [(1,Section Nothing Nothing)])]
244 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
245 , node0 [(This, [(1,Section Nothing (Just VeryGood))])]
248 [(This, [(1,[(Acceptable,1%4), (Good,1%2), (VeryGood,1%4)])])]
249 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
250 , node0 [(This, [(1,[(Good,1%1)])])]
251 , node0 [(This, [(1,[(VeryGood,1%1)])])]
253 , testSection "ErrorSection_invalid_shares sum not 1"
257 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
258 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
259 , node0 [(This, [(1,Section (Just $ 1%3) (Just Good))])]
261 (Left $ ErrorSection_invalid_shares [(This, [(1,[1%2,1%3])])])
262 , testSection "ErrorSection_invalid_shares negative share"
266 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
267 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
268 , node0 [(This, [(1,Section (Just $ -1%2) (Just Good))])]
270 (Left $ ErrorSection_invalid_shares [(This, [(1,[1%2,-1%2])])])
271 , testSection "2 judges, 3 grade, 3 sections (1 default)"
273 [(1::Int,ToReject), (2::Int,ToReject)]
275 [ (This, [(1,Section Nothing (Just Acceptable))])
278 [ (This, [(1,Section Nothing Nothing)])
281 [ (This, [(1,Section (Just $ 1%2) (Just Good))])
285 [ (This, [ (1,[(Acceptable,1%2), (Good,1%2)])
286 , (2,[(ToReject,1%1)])
290 [ (This, [ (1,[(Acceptable,1%1)])
291 , (2,[(ToReject,1%1)])
295 [ (This, [ (1,[(Good,1%1)])
296 , (2,[(ToReject,1%1)])
300 , testSection "2 judges, 4 grades, 5 sections (2 defaults)"
302 [(1::Int,ToReject), (2::Int,ToReject)]
304 [ (This, [(1,Section Nothing (Just Acceptable))])
307 [ (This, [(1,Section Nothing Nothing)])
310 [ (This, [(1,Section (Just $ 1%2) (Just Good))])
313 [ (This, [(1,Section Nothing (Just Good))])
316 [ (This, [ (1,Section Nothing (Just VeryGood))
317 , (2,Section Nothing (Just Insufficient))
323 [ (This, [ (1,[(Acceptable,1%4), (Good,1%2), (VeryGood,1%4)])
324 , (2,[(ToReject,2%3), (Insufficient,1%3)])
328 [ (This, [ (1,[(Acceptable,1%1)])
329 , (2,[(ToReject,1%1)])
333 [ (This, [ (1,[(Good,1%1)])
334 , (2,[(ToReject,1%1)])
338 [ (This, [ (1,[(VeryGood,1%1)])
339 , (2,[(Insufficient,1%1)])
343 [ (This, [ (1,[(VeryGood,1%1)])
344 , (2,[(Insufficient,1%1)])
349 , testSection "1 judge, default grade, 2 choices"
353 (Right $ node0 [ (This,[(1,[(ToReject,1%1)])])
354 , (That,[(1,[(ToReject,1%1)])])
356 , testSection "2 judges, 2 choices"
358 [(1::Int,ToReject), (2::Int,ToReject)]
362 [ (This, [(1,Section Nothing (Just Good))])
363 , (That, [(2,Section Nothing (Just Insufficient))])
366 [ (This, [(1,Section Nothing (Just Acceptable))])
367 , (That, [(2,Section Nothing (Just VeryGood))])
371 [ (This, [ (1,[(Good,1%2), (Acceptable,1%2)])
372 , (2,[(ToReject,1%1)])
374 , (That, [ (1,[(ToReject,1%1)])
375 , (2,[(Insufficient,1%2), (VeryGood,1%2)])
378 [ node0 [ (This, [ (1,[(Good,1%1)])
379 , (2,[(ToReject,1%1)])
381 , (That, [ (1,[(ToReject,1%1)])
382 , (2,[(Insufficient,1%1)])
385 , node0 [ (This, [ (1,[(Acceptable,1%1)])
386 , (2,[(ToReject,1%1)])
388 , (That, [ (1,[(ToReject,1%1)])
389 , (2,[(VeryGood,1%1)])
393 , testSection "1 judge, 1 choice"
397 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
399 , node0 [ (This, [(1,Section Nothing Nothing)])
403 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
405 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
407 , node0 [ (This, [(1,[(ToReject, 1%1)])])
410 , testSection "1 judge, 1 choice (missing judge)"
414 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
420 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
422 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
424 , node0 [ (This, [(1,[(ToReject, 1%1)])])
427 , testSection "1 judge, 1 choice (missing judge)"
433 , node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
437 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
439 [ node0 [ (This, [(1,[(ToReject, 1%1)])])
441 , node0 [ (This, [(1,[(Acceptable, 1%1)])])
444 , testSection "1 judge, 1 choice (missing choice)"
448 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
454 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
456 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
458 , node0 [ (This, [(1,[(ToReject, 1%1)])])
461 , testSection "1 judge, 1 choice (missing choice)"
467 , node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
471 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
473 [ node0 [ (This, [(1,[(ToReject, 1%1)])])
475 , node0 [ (This, [(1,[(Acceptable, 1%1)])])
478 , testSection "2 judges, 2 choices"
480 [(1::Int,ToReject), (2::Int,ToReject)]
482 [ (This, [(1,Section Nothing (Just Acceptable))])
483 , (That, [(2,Section (Just $ 1%8) (Just VeryGood))])
486 [ (This, [ (1,[(Acceptable,1%1)])
487 , (2,[(ToReject,1%1)])
489 , (That, [ (1,[(ToReject,1%1)])
490 , (2,[(VeryGood,1%1)])
493 , testSection "2 judges, 2 choices"
495 [(1::Int,ToReject), (2::Int,ToReject)]
499 [ (This, [(1,Section Nothing (Just Good))])
500 , (That, [(2,Section Nothing (Just Insufficient))])
503 [ (This, [(1,Section Nothing (Just Acceptable))])
504 , (That, [(2,Section (Just $ 1%8) (Just VeryGood))])
508 [ (This, [ (1,[(Good,1%2), (Acceptable,1%2)])
509 , (2,[(ToReject,1%1)])
511 , (That, [ (1,[(ToReject,1%1)])
512 , (2,[(Insufficient,7%8), (VeryGood,1%8)])
515 [ node0 [ (This, [ (1,[(Good,1%1)])
516 , (2,[(ToReject,1%1)])
518 , (That, [ (1,[(ToReject,1%1)])
519 , (2,[(Insufficient,1%1)])
522 , node0 [ (This, [ (1,[(Acceptable,1%1)])
523 , (2,[(ToReject,1%1)])
525 , (That, [ (1,[(ToReject,1%1)])
526 , (2,[(VeryGood,1%1)])
530 , testSection "2 judges, 2 choices"
532 [(1::Int,ToReject), (2::Int,ToReject)]
533 (Node [ (This, [(1,Section Nothing (Just Acceptable))])
535 [ node0 [ (This, [(1,Section Nothing Nothing)])
537 , node0 [ (This, [ (1,Section (Just $ 1%2) (Just Good)) ])
538 , (That, [ (1,Section (Just $ 1%3) Nothing)
539 , (2,Section (Just $ 1%5) (Just Insufficient))
542 , Node [ (This, [(1,Section Nothing (Just Good))])
543 , (That, [(2,Section Nothing (Just VeryGood))])
545 [ node0 [ (This, [ (1,Section Nothing (Just VeryGood))
546 , (2,Section Nothing (Just Insufficient))
548 , (That, [ (1,Section Nothing (Just Acceptable)) ])
550 , node0 [ (This, [ (1,Section Nothing (Just Acceptable))
552 , (That, [ (1,Section Nothing (Just VeryGood))
553 , (2,Section Nothing (Just Good))
559 Node [ (This, [ (1,[(Acceptable,1%4 + 1%8), (Good,1%2), (VeryGood,1%8)])
560 , (2,[(ToReject,1%3 + 1%3 + 1%6), (Insufficient,1%6)])
562 , (That, [ (1,[(ToReject,1%3 + 1%3), (Acceptable,1%6), (VeryGood,1%6)])
563 , (2,[(ToReject,4%10), (Insufficient,1%5), (VeryGood,4%20), (Good,4%20)])
566 [ node0 [ (This, [ (1,[(Acceptable,1%1)]) -- 1%4
567 , (2,[(ToReject,1%1)]) -- 1%3
569 , (That, [ (1,[(ToReject,1%1)]) -- 1%3
570 , (2,[(ToReject,1%1)]) -- 4%10
573 , node0 [ (This, [ (1,[(Good,1%1)]) -- 1%2
574 , (2,[(ToReject,1%1)]) -- 1%3
576 , (That, [ (1,[(ToReject,1%1)]) -- 1%3
577 , (2,[(Insufficient,1%1)]) -- 1%5
580 , Node [ (This, [ (1,[(VeryGood,1%2), (Acceptable,1%2)]) -- 1%4
581 , (2,[(Insufficient,1%2), (ToReject,1%2)]) -- 1%3
583 , (That, [ (1,[(Acceptable,1%2), (VeryGood,1%2)]) -- 1%3
584 , (2,[(VeryGood,1%2), (Good,1%2)]) -- 4%10
587 [ node0 [ (This, [ (1,[(VeryGood,1%1)])
588 , (2,[(Insufficient,1%1)])
590 , (That, [ (1,[(Acceptable,1%1)])
591 , (2,[(VeryGood,1%1)])
594 , node0 [ (This, [ (1,[(Acceptable,1%1)])
595 , (2,[(ToReject,1%1)])
597 , (That, [ (1,[(VeryGood,1%1)])
608 elide :: String -> String
609 elide s | length s > 42 = take 42 s ++ ['…']
612 mkMerit :: (Ord grade, Show grade) => [grade] -> [Share] -> Merit grade
613 mkMerit gs = fromList . (gs`zip`)
616 (Eq choice, Hashable choice, Ord grade) =>
617 [(choice,[grade])] ->
618 MeritByChoice choice grade
620 meritByChoice $ fromList $
621 second (fromList . zip [1::Int ..] . (singleGrade <$>)) <$> os
623 testCompareValue :: (Ord grade, Show grade) =>
624 [(grade, Share)] -> [(grade, Share)] -> TestTree
625 testCompareValue x y =
626 testGroup (elide $ show (x,y))
627 [ testCase "x == x" $ MajorityValue x`compare`MajorityValue x @?= EQ
628 , testCase "y == y" $ MajorityValue y`compare`MajorityValue y @?= EQ
629 , testCase "x < y" $ MajorityValue x`compare`MajorityValue y @?= LT
630 , testCase "y > x" $ MajorityValue y`compare`MajorityValue x @?= GT
633 testMajorityRanking ::
634 (Eq choice, Hashable choice, Ord grade, Show grade, Show choice) =>
635 [(choice, [grade])] ->
636 MajorityRanking choice grade -> TestTree
637 testMajorityRanking os expect =
638 testCase (elide $ show os) $
639 majorityRanking (mkMeritByChoice os) @?= expect
641 testMajorityValueOfOpinions ::
642 (Show grade, Show choice, Ord grade, Eq choice, Hashable choice) =>
643 [(choice, [grade])] ->
644 [(choice, [(grade,Share)])] -> TestTree
645 testMajorityValueOfOpinions os expect =
646 testCase (elide $ show os) $
647 majorityValueByChoice (mkMeritByChoice os)
648 @?= (MajorityValue<$>HM.fromList expect)
650 testMajorityValueOfMerits ::
651 (Show grade, Show choice, Ord grade, Eq choice, Hashable choice) =>
652 MeritByChoice choice grade ->
653 [(choice, [(grade,Share)])] -> TestTree
654 testMajorityValueOfMerits ms expect =
655 testCase (elide $ show ms) $
656 majorityValueByChoice ms
657 @?= (MajorityValue<$>HM.fromList expect)
670 Judges judge grade ->
671 Tree (SectionNode choice judge grade) ->
672 Either (ErrorSection choice judge grade)
673 (Tree (OpinionsByChoice choice judge grade)) ->
675 testSection msg cs js ss expect =
676 testCase (elide msg) $
677 opinionsBySection cs js ss @?= expect
682 instance (Eq choice, Hashable choice) => IsList (SectionNode choice judge grade) where
683 type Item (SectionNode choice judge grade) = (choice, SectionByJudge judge grade)
684 fromList = SectionNode Nothing . fromList
685 toList = GHC.Exts.toList . sectionByJudgeByChoice