packages feed

hjugement-2.0.0.20180903: test/HUnit.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module HUnit where

import Test.Tasty
import Test.Tasty.HUnit

import Control.Arrow (second)
import Data.Hashable (Hashable)
import Data.Ratio ((%))
import Data.Tree (Tree(..))
import GHC.Exts (IsList(..))
import Prelude
import qualified Data.HashMap.Strict as HM

import Majority.Judgment
import Types

hunits :: TestTree
hunits =
	testGroup "HUnit"
	 [ testGroup "MajorityValue" $
		 [ testCompareValue
			 (majorityValue $ Merit [(3,15), (2,7), (1,3), (0::Int,2)])
			 (majorityValue $ Merit [(3,16), (2,6), (1,2), (0,3)])
		 , testGroup "Merit"
			 [  let m = mkMerit ['A'..'F'] in
				testMajorityValueOfMerits
				 [ (The, m [136,307,251,148,84,74])
				 ]
				 [ (The,
					 [ Middle ( 57 % 1) 'C' 'C'
					 , Middle (137 % 1) 'B' 'C'
					 , Middle (148 % 1) 'B' 'D'
					 , Middle ( 22 % 1) 'B' 'E'
					 , Middle ( 62 % 1) 'A' 'E'
					 , Middle ( 74 % 1) 'A' 'F'
					 ])
				 ]
			 , let m = mkMerit [ToReject .. TooGood] in
				testMajorityValueOfMerits
				 [ (This, m [12,10,21,5,5,5,2])
				 , (That, m [12,16,22,3,3,3,1])
				 ]
				 [ (This, [ Middle (8 % 1) Acceptable   Acceptable
				          , Middle (5 % 1) Insufficient Acceptable
				          , Middle (5 % 1) Insufficient Good
				          , Middle (5 % 1) ToReject     VeryGood
				          , Middle (5 % 1) ToReject     Perfect
				          , Middle (2 % 1) ToReject     TooGood
				          ])
				 , (That, [ Middle ( 2 % 1) Acceptable   Acceptable
				          , Middle (16 % 1) Insufficient Acceptable
				          , Middle ( 2 % 1) ToReject     Acceptable
				          , Middle ( 3 % 1) ToReject     Good
				          , Middle ( 3 % 1) ToReject     VeryGood
				          , Middle ( 3 % 1) ToReject     Perfect
				          , Middle ( 1 % 1) ToReject     TooGood
				          ])
				 ]
			 ]
		 , testGroup "MajorityRanking"
			 [ testMajorityValueOfOpinions
				 [ (The, [No,No,No,No,Yes,Yes]) ]
				 [ (The, [ Middle (1 % 1) No No
				         , Middle (2 % 1) No Yes
				         ]) ]
			 , testMajorityValueOfOpinions
				 [ (The, [No,No,No,Yes,Yes,Yes]) ]
				 [ (The, [ Middle (3 % 1) No Yes ]) ]
			 , testMajorityValueOfOpinions
				 [ (The, [No,No,No,No,Yes,Yes,Yes]) ]
				 [ (The, [ Middle (1 % 2) No No
				         , Middle (3 % 1) No Yes ]) ]
			 , testMajorityValueOfOpinions
				 [ (This, [No,No,No,No,Yes,Yes])
				 , (That, [No,Yes,Yes,Yes,Yes,Yes])
				 ]
				 [ (This, [ Middle (1 % 1) No No
				          , Middle (2 % 1) No Yes
				          ])
				 , (That, [ Middle (2 % 1) Yes Yes
				          , Middle (1 % 1) No  Yes
				          ])
				 ]
			 , testMajorityValueOfOpinions
				 [ (This, [No,No,No,No,No,No])
				 , (That, [No,No,No,Yes,Yes,Yes])
				 ]
				 [ (This, [Middle (3 % 1) No No])
				 , (That, [Middle (3 % 1) No Yes])
				 ]
			 , testMajorityValueOfOpinions
				 [ (This, [Yes,Yes,Yes,Yes,Yes,Yes])
				 , (That, [No,No,No,Yes,Yes,Yes])
				 ]
				 [ (This, [Middle (3 % 1) Yes Yes])
				 , (That, [Middle (3 % 1) No  Yes])
				 ]
			 , testMajorityValueOfOpinions
				 [ (This, [No,No,Yes,Yes,Yes,Yes])
				 , (That, [No,No,No,Yes,Yes,Yes])
				 ]
				 [ (This, [ Middle (1 % 1) Yes Yes
				          , Middle (2 % 1) No  Yes
				          ])
				 , (That, [ Middle (3 % 1) No Yes ])
				 ]
			 , testMajorityValueOfOpinions
				 [ (1::Int, [Perfect,Perfect,VeryGood,Perfect,Perfect,Perfect])
				 , (2, [Perfect,VeryGood,VeryGood,VeryGood,Good,VeryGood])
				 , (3, [Acceptable,Perfect,Good,VeryGood,VeryGood,Perfect])
				 , (4, [VeryGood,Good,Acceptable,Good,Good,Good])
				 , (5, [Good,Acceptable,VeryGood,Good,Good,Good])
				 , (6, [VeryGood,Acceptable,Insufficient,Acceptable,Acceptable,Good])
				 ]
				 [ (1, [ Middle (2 % 1) Perfect      Perfect
				       , Middle (1 % 1) VeryGood     Perfect
				       ])
				 , (2, [ Middle (2 % 1) VeryGood     VeryGood
				       , Middle (1 % 1) Good         Perfect
				       ])
				 , (3, [ Middle (1 % 1) VeryGood     VeryGood
				       , Middle (1 % 1) Good         Perfect
				       , Middle (1 % 1) Acceptable   Perfect
				       ])
				 , (4, [ Middle (2 % 1) Good         Good
				       , Middle (1 % 1) Acceptable   VeryGood
				       ])
				 , (5, [ Middle (2 % 1) Good         Good
				       , Middle (1 % 1) Acceptable   VeryGood
				       ])
				 , (6, [ Middle (1 % 1) Acceptable   Acceptable
				       , Middle (1 % 1) Acceptable   Good
				       , Middle (1 % 1) Insufficient VeryGood
				       ])
				 ]
			 ]
		 , testGroup "Section"
			 [ testSection "0 judge"
				 ([]::Choices C2)
				 ([]::Judges Int G6)
				 (node0 [])
				 (Right $ node0 [])
			 , testSection "1 judge, default grade"
				 [This]
				 [(1::Int,ToReject)]
				 (node0 [])
				 (Right $ node0 [(This, [(1,[(ToReject,1%1)])])])
			 , testSection "1 judge, default grade, 2 choices"
				 [This, That]
				 [(1::Int,ToReject)]
				 (node0 [])
				 (Right $ node0 [ (This, [(1,[(ToReject,1%1)])])
				                , (That, [(1,[(ToReject,1%1)])])
				                ])
			 , testSection "1 judge, default grade"
				 [This]
				 [(1::Int,ToReject)]
				 (node0 [(This,[(1,Section Nothing Nothing)])])
				 (Right $ node0 [(This,[(1,[(ToReject,1%1)])])])
			 , testSection "2 judges, default grade"
				 [This]
				 [(1::Int,ToReject), (2::Int,ToReject)]
				 (node0
					 [ (This, [(1,Section Nothing Nothing)])
					 ])
				 (Right $ node0
					 [ (This, [ (1,[(ToReject,1%1)])
					          , (2,[(ToReject,1%1)])
					          ])
					 ])
			 , testSection "ErrorSection_unknown_choices"
				 []
				 [(1::Int,ToReject)]
				 (node0 [(This,[])])
				 (Left $ ErrorSection_unknown_choices [This])
			 , testSection "ErrorSection_unknown_choices"
				 []
				 [(1::Int,ToReject)]
				 (node0 [(This,[(2,Section Nothing Nothing)])])
				 (Left $ ErrorSection_unknown_choices [This])
			 , testSection "ErrorSection_unknown_choices"
				 [This]
				 [(1::Int,ToReject)]
				 (node0 [ (This,[(1,Section Nothing Nothing)])
				        , (That,[(2,Section Nothing Nothing)])
				        ])
				 (Left $ ErrorSection_unknown_choices [That])
			 , testSection "ErrorSection_unknown_judges"
				 [This]
				 [(1::Int,ToReject)]
				 (node0 [(This,[(2,Section Nothing Nothing)])])
				 (Left $ ErrorSection_unknown_judges [(This,[2])])
			 , testSection "1 judge, 1 grade"
				 [This]
				 [(1::Int,ToReject)]
				 (node0 [(This,[(1,Section Nothing (Just Acceptable))])])
				 (Right $ node0 [(This,[(1,[(Acceptable,1%1)])])])
			 , testSection "1 judge, 1 grade, 2 sections"
				 [This]
				 [(1::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
					 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
					 , node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
					 ])
				 (Right $ Node
					 [ (This, [(1,[(Acceptable,1%1)])]) ]
					 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
					 , node0 [(This, [(1,[(Acceptable,1%1)])])]
					 ])
			 , testSection "sectionNodeShare with judge"
				 [This]
				 [(1::Int,ToReject), (2,Insufficient)]
				 (Node
					 [(This, [(1,Section Nothing (Just Acceptable))])]
					 [ node0 $ SectionNode (Just $ 1%3) [(This, [ (1,Section (Just $ 1%2) Nothing)
					                                            , (2,Section Nothing Nothing)
					                                            ])]
					 , node0                            [(This, [ (1,Section (Just $ 1%2) Nothing)
					                                            , (2,Section Nothing (Just Good))
					                                            ])]
					 ])
				 (Right $ Node
					 [ (This, [ (1,[(Acceptable,1%2 + 1%2)])
					          , (2,[(Insufficient,1%3), (Good,2%3)])
					          ]) ]
					 [ node0 [(This, [ (1,[(Acceptable,1%1)])
					                 , (2,[(Insufficient,1%1)])
					                 ])]
					 , node0 [(This, [ (1,[(Acceptable,1%1)])
					                 , (2,[(Good,1%1)])
					                 ])]
					 ])
			 , testSection "sectionNodeShare without judge"
				 [This]
				 [(1::Int,ToReject), (2,Insufficient)]
				 (Node
					 [(This, [(1,Section Nothing (Just Acceptable))])]
					 [ node0 $ SectionNode (Just $ 1%3) [(This, [ (1,Section (Just $ 1%2) Nothing) ])]
					 , node0                            [(This, [ (1,Section (Just $ 1%2) Nothing)
					                                            , (2,Section Nothing (Just Good))
					                                            ])]
					 ])
				 (Right $ Node
					 [ (This, [ (1,[(Acceptable,1%2 + 1%2)])
					          , (2,[(Insufficient,1%3), (Good,2%3)])
					          ]) ]
					 [ node0 [(This, [ (1,[(Acceptable,1%1)])
					                 , (2,[(Insufficient,1%1)])
					                 ])]
					 , node0 [(This, [ (1,[(Acceptable,1%1)])
					                 , (2,[(Good,1%1)])
					                 ])]
					 ])
			 , testSection "1 judge, 2 grades, 2 sections"
				 [This]
				 [(1::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
					 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
					 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
					 ])
				 (Right $ Node
					 [(This, [(1,[(Acceptable,1%2), (Good,1%2)])])]
					 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
					 , node0 [(This, [(1,[(Good,1%1)])])]
					 ])
			 , testSection "1 judge, 2 grades, 2 sections (1 default)"
				 [This]
				 [(1::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
					 [ node0 [(This, [(1,Section Nothing Nothing)])]
					 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
					 ])
				 (Right $ Node
					 [(This, [(1,[(Acceptable,1%2), (Good,1%2)])])]
					 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
					 , node0 [(This, [(1,[(Good,1%1)])])]
					 ])
			 , testSection "1 judge, 3 grades, 3 sections (2 default)"
				 [This]
				 [(1::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
					 [ node0 [(This, [(1,Section Nothing Nothing)])]
					 , node0 [(This, [(1,Section (Just $ 1%2) (Just Good))])]
					 , node0 [(This, [(1,Section Nothing (Just VeryGood))])]
					 ])
				 (Right $ Node
					 [(This, [(1,[(Acceptable,1%4), (Good,1%2), (VeryGood,1%4)])])]
					 [ node0 [(This, [(1,[(Acceptable,1%1)])])]
					 , node0 [(This, [(1,[(Good,1%1)])])]
					 , node0 [(This, [(1,[(VeryGood,1%1)])])]
					 ])
			 , testSection "ErrorSection_invalid_shares sum not 1"
				 [This]
				 [(1::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
					 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
					 , node0 [(This, [(1,Section (Just $ 1%3) (Just Good))])]
					 ])
				 (Left $ ErrorSection_invalid_shares [(This, [(1,[1%2,1%3])])])
			 , testSection "ErrorSection_invalid_shares negative share"
				 [This]
				 [(1::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))]) ]
					 [ node0 [(This, [(1,Section (Just $ 1%2) Nothing)])]
					 , node0 [(This, [(1,Section (Just $ -1%2) (Just Good))])]
					 ])
				 (Left $ ErrorSection_invalid_shares [(This, [(1,[1%2,-1%2])])])
			 , testSection "2 judges, 3 grade, 3 sections (1 default)"
				 [This]
				 [(1::Int,ToReject), (2::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))])
					 ]
					 [ node0
						 [ (This, [(1,Section Nothing Nothing)])
						 ]
					 , node0
						 [ (This, [(1,Section (Just $ 1%2) (Just Good))])
						 ]
					 ])
				 (Right $ Node
					 [ (This, [ (1,[(Acceptable,1%2), (Good,1%2)])
					          , (2,[(ToReject,1%1)])
					          ])
					 ]
					 [ node0
						 [ (This, [ (1,[(Acceptable,1%1)])
						          , (2,[(ToReject,1%1)])
						          ])
						 ]
					 , node0
						 [ (This, [ (1,[(Good,1%1)])
						          , (2,[(ToReject,1%1)])
						          ])
						 ]
					 ])
			 , testSection "2 judges, 4 grades, 5 sections (2 defaults)"
				 [This]
				 [(1::Int,ToReject), (2::Int,ToReject)]
				 (Node
					 [ (This, [(1,Section Nothing (Just Acceptable))])
					 ]
					 [ node0
						 [ (This, [(1,Section Nothing Nothing)])
						 ]
					 , node0
						 [ (This, [(1,Section (Just $ 1%2) (Just Good))])
						 ]
					 , Node
						 [ (This, [(1,Section Nothing (Just Good))])
						 ]
						 [ node0
							 [ (This, [ (1,Section Nothing (Just VeryGood))
							          , (2,Section Nothing (Just Insufficient))
							          ])
							 ]
						 ]
					 ])
				 (Right $ Node
					 [ (This, [ (1,[(Acceptable,1%4), (Good,1%2), (VeryGood,1%4)])
					          , (2,[(ToReject,2%3), (Insufficient,1%3)])
					          ])
					 ]
					 [ node0
						 [ (This, [ (1,[(Acceptable,1%1)])
						          , (2,[(ToReject,1%1)])
						          ])
						 ]
					 , node0
						 [ (This, [ (1,[(Good,1%1)])
						          , (2,[(ToReject,1%1)])
						          ])
						 ]
					 , Node
						 [ (This, [ (1,[(VeryGood,1%1)])
						          , (2,[(Insufficient,1%1)])
						          ])
						 ]
						 [ node0
							 [ (This, [ (1,[(VeryGood,1%1)])
							          , (2,[(Insufficient,1%1)])
							          ])
							 ]
						 ]
					 ])
			 , testSection "1 judge, default grade, 2 choices"
				 [This, That]
				 [(1::Int,ToReject)]
				 (node0 [])
				 (Right $ node0 [ (This,[(1,[(ToReject,1%1)])])
				                , (That,[(1,[(ToReject,1%1)])])
				                ])
			 , testSection "2 judges, 2 choices"
				 [This, That]
				 [(1::Int,ToReject), (2::Int,ToReject)]
				 (Node
					 [ ]
					 [ node0
						 [ (This, [(1,Section Nothing (Just Good))])
						 , (That, [(2,Section Nothing (Just Insufficient))])
						 ]
					 , node0
						 [ (This, [(1,Section Nothing (Just Acceptable))])
						 , (That, [(2,Section Nothing (Just VeryGood))])
						 ]
					 ])
				 (Right $ Node
					 [ (This, [ (1,[(Good,1%2), (Acceptable,1%2)])
					          , (2,[(ToReject,1%1)])
					          ])
					 , (That, [ (1,[(ToReject,1%1)])
					          , (2,[(Insufficient,1%2), (VeryGood,1%2)])
					          ])
					 ]
					 [ node0 [ (This, [ (1,[(Good,1%1)])
					                  , (2,[(ToReject,1%1)])
					                  ])
					         , (That, [ (1,[(ToReject,1%1)])
					                  , (2,[(Insufficient,1%1)])
					                  ])
					         ]
					 , node0 [ (This, [ (1,[(Acceptable,1%1)])
					                  , (2,[(ToReject,1%1)])
					                  ])
					         , (That, [ (1,[(ToReject,1%1)])
					                  , (2,[(VeryGood,1%1)])
					                  ])
					         ]
					 ])
			 , testSection "1 judge, 1 choice"
				 [This]
				 [(1::Int,ToReject)]
				 (Node []
					 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
					         ]
					 , node0 [ (This, [(1,Section Nothing Nothing)])
					         ]
					 ])
				 (Right $ Node
					 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
					 ]
					 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
					         ]
					 , node0 [ (This, [(1,[(ToReject, 1%1)])])
					         ]
					 ])
			 , testSection "1 judge, 1 choice (missing judge)"
				 [This]
				 [(1::Int,ToReject)]
				 (Node []
					 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
					         ]
					 , node0 [ (This, [])
					         ]
					 ])
				 (Right $ Node
					 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
					 ]
					 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
					         ]
					 , node0 [ (This, [(1,[(ToReject, 1%1)])])
					         ]
					 ])
			 , testSection "1 judge, 1 choice (missing judge)"
				 [This]
				 [(1::Int,ToReject)]
				 (Node []
					 [ node0 [ (This, [])
					         ]
					 , node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
					         ]
					 ])
				 (Right $ Node
					 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
					 ]
					 [ node0 [ (This, [(1,[(ToReject, 1%1)])])
					         ]
					 , node0 [ (This, [(1,[(Acceptable, 1%1)])])
					         ]
					 ])
			 , testSection "1 judge, 1 choice (missing choice)"
				 [This]
				 [(1::Int,ToReject)]
				 (Node []
					 [ node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
					         ]
					 , node0 [
					         ]
					 ])
				 (Right $ Node
					 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
					 ]
					 [ node0 [ (This, [(1,[(Acceptable, 1%1)])])
					         ]
					 , node0 [ (This, [(1,[(ToReject, 1%1)])])
					         ]
					 ])
			 , testSection "1 judge, 1 choice (missing choice)"
				 [This]
				 [(1::Int,ToReject)]
				 (Node []
					 [ node0 [ 
					         ]
					 , node0 [ (This, [(1,Section (Just $ 1%8) (Just Acceptable))])
					         ]
					 ])
				 (Right $ Node
					 [ (This, [(1,[(Acceptable,1%8), (ToReject,7%8)])])
					 ]
					 [ node0 [ (This, [(1,[(ToReject, 1%1)])])
					         ]
					 , node0 [ (This, [(1,[(Acceptable, 1%1)])])
					         ]
					 ])
			 , testSection "2 judges, 2 choices"
				 [This, That]
				 [(1::Int,ToReject), (2::Int,ToReject)]
				 (node0
					 [ (This, [(1,Section Nothing (Just Acceptable))])
					 , (That, [(2,Section (Just $ 1%8) (Just VeryGood))])
					 ])
				 (Right $ node0
					 [ (This, [ (1,[(Acceptable,1%1)])
					          , (2,[(ToReject,1%1)])
					          ])
					 , (That, [ (1,[(ToReject,1%1)])
					          , (2,[(VeryGood,1%1)])
					          ])
					 ])
			 , testSection "2 judges, 2 choices"
				 [This, That]
				 [(1::Int,ToReject), (2::Int,ToReject)]
				 (Node
					 [ ]
					 [ node0
						 [ (This, [(1,Section Nothing (Just Good))])
						 , (That, [(2,Section Nothing (Just Insufficient))])
						 ]
					 , node0
						 [ (This, [(1,Section Nothing (Just Acceptable))])
						 , (That, [(2,Section (Just $ 1%8) (Just VeryGood))])
						 ]
					 ])
				 (Right $ Node
					 [ (This, [ (1,[(Good,1%2), (Acceptable,1%2)])
					          , (2,[(ToReject,1%1)])
					          ])
					 , (That, [ (1,[(ToReject,1%1)])
					          , (2,[(Insufficient,7%8), (VeryGood,1%8)])
					          ])
					 ]
					 [ node0 [ (This, [ (1,[(Good,1%1)])
					                  , (2,[(ToReject,1%1)])
					                  ])
					         , (That, [ (1,[(ToReject,1%1)])
					                  , (2,[(Insufficient,1%1)])
					                  ])
					         ]
					 , node0 [ (This, [ (1,[(Acceptable,1%1)])
					                  , (2,[(ToReject,1%1)])
					                  ])
					         , (That, [ (1,[(ToReject,1%1)])
					                  , (2,[(VeryGood,1%1)])
					                  ])
					         ]
					 ])
			 , testSection "2 judges, 2 choices"
				 [This, That]
				 [(1::Int,ToReject), (2::Int,ToReject)]
				 (Node [ (This, [(1,Section Nothing (Just Acceptable))])
				       ]
				       [ node0 [ (This, [(1,Section Nothing Nothing)])
				               ]
				       , node0 [ (This, [ (1,Section (Just $ 1%2) (Just Good)) ])
				               , (That, [ (1,Section (Just $ 1%3) Nothing)
				                        , (2,Section (Just $ 1%5) (Just Insufficient))
				                        ])
				               ]
				       , Node [ (This, [(1,Section Nothing (Just Good))])
				              , (That, [(2,Section Nothing (Just VeryGood))])
				              ]
				              [ node0 [ (This, [ (1,Section Nothing (Just VeryGood))
				                               , (2,Section Nothing (Just Insufficient))
				                               ])
				                      , (That, [ (1,Section Nothing (Just Acceptable)) ])
				                      ]
				              , node0 [ (This, [ (1,Section Nothing (Just Acceptable))
				                               ])
				                      , (That, [ (1,Section Nothing (Just VeryGood))
				                               , (2,Section Nothing (Just Good))
				                               ])
				                      ]
				              ]
				       ])
				 (Right $
					Node [ (This, [ (1,[(Acceptable,1%4 + 1%8), (Good,1%2), (VeryGood,1%8)])
					              , (2,[(ToReject,1%3 + 1%3 + 1%6), (Insufficient,1%6)])
					              ])
					     , (That, [ (1,[(ToReject,1%3 + 1%3), (Acceptable,1%6), (VeryGood,1%6)])
					              , (2,[(ToReject,4%10), (Insufficient,1%5), (VeryGood,4%20), (Good,4%20)])
					              ])
					     ]
					     [ node0 [ (This, [ (1,[(Acceptable,1%1)]) -- 1%4
					                      , (2,[(ToReject,1%1)])   -- 1%3
					                      ])
					             , (That, [ (1,[(ToReject,1%1)])   -- 1%3
					                      , (2,[(ToReject,1%1)])   -- 4%10
					                      ])
					             ]
					     , node0 [ (This, [ (1,[(Good,1%1)])         -- 1%2
					                      , (2,[(ToReject,1%1)])     -- 1%3
					                      ])
					             , (That, [ (1,[(ToReject,1%1)])     -- 1%3
					                      , (2,[(Insufficient,1%1)]) -- 1%5
					                      ])
					             ]
					     , Node [ (This, [ (1,[(VeryGood,1%2), (Acceptable,1%2)])   -- 1%4
					                     , (2,[(Insufficient,1%2), (ToReject,1%2)]) -- 1%3
					                     ])
					            , (That, [ (1,[(Acceptable,1%2), (VeryGood,1%2)])   -- 1%3
					                     , (2,[(VeryGood,1%2), (Good,1%2)])         -- 4%10
					                     ])
					            ]
					            [ node0 [ (This, [ (1,[(VeryGood,1%1)])
					                             , (2,[(Insufficient,1%1)])
					                             ])
					                    , (That, [ (1,[(Acceptable,1%1)])
					                             , (2,[(VeryGood,1%1)])
					                             ])
					                    ]
					            , node0 [ (This, [ (1,[(Acceptable,1%1)])
					                             , (2,[(ToReject,1%1)])
					                             ])
					                    , (That, [ (1,[(VeryGood,1%1)])
					                             , (2,[(Good,1%1)])
					                             ])
					                    ]
					            ]
					     ]
				 )
			 ]
		 ]
	 ]

elide :: String -> String
elide s | length s > 42 = take 42 s ++ ['…']
        | otherwise = s

mkMerit :: (Ord grade, Show grade) => [grade] -> [Share] -> Merit grade
mkMerit gs = fromList . (gs`zip`)

mkMeritByChoice ::
 (Eq choice, Hashable choice, Ord grade) =>
 [(choice,[grade])] ->
 MeritByChoice choice grade
mkMeritByChoice os =
	meritByChoice $ fromList $
	second (fromList . zip [1::Int ..] . (singleGrade <$>)) <$> os

testCompareValue :: (Ord grade, Show grade) =>
                    MajorityValue grade -> MajorityValue grade -> TestTree
testCompareValue x y =
	testGroup (elide $ show (unMajorityValue x, unMajorityValue y))
	 [ testCase "x == x" $ x`compare`x @?= EQ
	 , testCase "y == y" $ y`compare`y @?= EQ
	 , testCase "x <  y" $ x`compare`y @?= LT
	 , testCase "y >  x" $ y`compare`x @?= GT
	 ]

testMajorityRanking ::
 (Eq choice, Hashable choice, Ord grade, Show grade, Show choice) =>
 [(choice, [grade])] ->
 MajorityRanking choice grade -> TestTree
testMajorityRanking os expect =
	testCase (elide $ show os) $
		majorityRanking (mkMeritByChoice os) @?= expect

testMajorityValueOfOpinions ::
 (Show grade, Show choice, Ord grade, Eq choice, Hashable choice) =>
 [(choice, [grade])] ->
 [(choice, [Middle grade])] -> TestTree
testMajorityValueOfOpinions os expect =
	testCase (elide $ show os) $
		majorityValueByChoice (mkMeritByChoice os)
		 @?= (MajorityValue<$>HM.fromList expect)

testMajorityValueOfMerits ::
 (Show grade, Show choice, Ord grade, Eq choice, Hashable choice) =>
 MeritByChoice choice grade ->
 [(choice, [Middle grade])] -> TestTree
testMajorityValueOfMerits ms expect =
	testCase (elide $ show $ unMeritByChoice ms) $
		majorityValueByChoice ms
		 @?= (MajorityValue<$>HM.fromList expect)

testSection ::
 Eq choice =>
 Hashable choice =>
 Eq judge =>
 Hashable judge =>
 Ord grade =>
 Show choice =>
 Show judge =>
 Show grade =>
 String ->
 Choices choice ->
 Judges judge grade ->
 Tree (SectionNode choice judge grade) ->
 Either (ErrorSection choice judge grade)
        (Tree (OpinionsByChoice choice judge grade)) ->
 TestTree
testSection msg cs js ss expect =
	testCase (elide msg) $
		opinionsBySection cs js ss @?= expect

node0 :: a -> Tree a
node0 = (`Node`[])

instance (Eq choice, Hashable choice) => IsList (SectionNode choice judge grade) where
	type Item (SectionNode choice judge grade) = (choice, SectionByJudge judge grade)
	fromList = SectionNode Nothing . fromList
	toList = GHC.Exts.toList . sectionByJudgeByChoice