summaryrefslogtreecommitdiff
path: root/edit-lens/test/Control/DFSTTest.hs
blob: 4d91a0312d0f05f8bf0f5fdc4f20815787548f1a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
module Control.DFSTTest where

import Control.DFST
import Control.FST hiding (stInitial, stTransition, stAccept)

import Data.Set (Set)
import qualified Data.Set as Set

import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map

import Data.Sequence (Seq)
import qualified Data.Sequence as Seq

import Data.Maybe (maybeToList)

import Test.Tasty
import Test.Tasty.Hedgehog
import Test.Tasty.HUnit hiding (assert)

import Hedgehog
import qualified Hedgehog.Gen as G
import qualified Hedgehog.Range as R

import Numeric.Natural

import Text.PrettyPrint.Leijen (Pretty(..))


dfstId :: Ord a => Set a -> DFST () a a
dfstId syms = DFST
  { stInitial = ()
  , stTransition = Map.fromList
    [(((), sym), ((), Seq.singleton sym)) | sym <- Set.toList syms]
  , stAccept = Set.singleton ()
  }

dfstDouble :: Ord a => Set a -> DFST () a a
dfstDouble syms = DFST
  { stInitial = ()
  , stTransition = Map.fromList 
    [(((), sym), ((), Seq.fromList [sym, sym])) | sym <- Set.toList syms]
  , stAccept = Set.singleton ()
  }

dfstRunLengthDecode :: Ord a
                    => Set a
                    -> Natural
                    -> DFST (Maybe Natural) (Either Natural a) a
dfstRunLengthDecode syms lim = DFST
  { stInitial = Nothing
  , stTransition = Map.fromList . concat $
    [ [((Nothing, Left n), (Just n, Seq.empty)) | n <- [0..lim]]
    , [((Just n, Right sym), (Nothing, Seq.replicate (fromIntegral n) sym)) | n <- [0..lim], sym <- Set.toList syms]
    ]
  , stAccept = Set.singleton Nothing
  }

genWord :: Gen [Natural]
genWord = G.list (R.linear 0 1000) . G.integral $ R.linear 0 100

genDFST :: (Ord input, Ord output) => Set input -> Set output -> Gen (DFST Natural input output)
genDFST inA outA = do
  states <- G.set (R.linear 1 1000) . G.integral $ R.linear 0 100
  stInitial <- G.element $ Set.toList states
  stAccept <- Set.fromList <$> G.subsequence (Set.toList states)
  stTransition <- fmap Map.fromList . G.list (R.linear 0 1000) . G.small $ do
    st <- G.element $ Set.toList states
    input <- G.element $ Set.toList inA
    st' <- G.element $ Set.toList states
    output <- fmap Seq.fromList . G.list (R.linear 0 20) . G.element $ Set.toList outA
    return ((st, input), (st', output))
  return DFST{..}
    

testDFST :: (Show output, Ord output, Show state, Ord state) => (Set Natural -> DFST state Natural output) -> (Seq Natural -> Seq output) -> Property
testDFST mkDfst f = property $ do
  input <- forAll genWord
  let fst = mkDfst $ Set.fromList input
  Just (f $ Seq.fromList input) === runDFST fst (Seq.fromList input)

hprop_runDFSTId, hprop_runDFSTDouble :: Property
hprop_runDFSTId = testDFST dfstId id
hprop_runDFSTDouble = testDFST dfstDouble double
  where
    double :: Seq a -> Seq a
    double Seq.Empty = Seq.Empty
    double (a Seq.:<| as) = a Seq.:<| a Seq.:<| double as

unit_runLengthDecode :: Assertion
unit_runLengthDecode = runDFST dfst input @?= Just (Seq.fromList "aaacc")
  where
    input = Seq.fromList [Left 3, Right 'a', Left 0, Right 'b', Left 2, Right 'c']
    dfst = dfstRunLengthDecode (Set.fromList "abc") 3

hprop_toFST :: Property
hprop_toFST = property $ do
  input <- forAll genWord
  dfst <- forAllWith (show . pretty . toFST) $ genDFST (Set.fromList $ input ++ [0..20]) (Set.fromList [0..20] :: Set Natural)
  
  runFST (toFST dfst) (Seq.fromList input) === maybeToList (runDFST dfst $ Seq.fromList input)