{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE GADTs #-}
# LANGUAGE TemplateHaskell #
# LANGUAGE TypeFamilies #
# OPTIONS_GHC -Wno - orphans #
{-# OPTIONS_GHC -Wno-unused-top-binds #-}

module Clash.Tests.AsyncFIFOSynchronizer (tests) where

import Data.Maybe (isJust, catMaybes)
import qualified Prelude as P

import Hedgehog as H
import qualified Hedgehog.Range as Range
import qualified Hedgehog.Gen as Gen
import Test.Tasty
import Test.Tasty.Hedgehog.Extra
import Test.Tasty.HUnit

import Clash.Explicit.Prelude

createDomain vSystem{vName = "Slow", vPeriod = 2 * vPeriod vSystem}

sync12RW
  :: ( KnownDomain dom1
     , KnownDomain dom2
     )
  => Int
  -> Int
  -> Int
  -> ( Signal dom1 (Bool, (Int, Bool))
     , Signal dom2 (Maybe Int, Bool)
     )
sync12RW w1 w2 w3 =
  ( bundle (inputRInc, bundle (rdata, rempty))
  , bundle (inputWDataM, wfull)
  )
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d4 clockGen clockGen resetGen resetGen enableGen
      enableGen inputRInc inputWDataM
  inputRInc = fromList $ P.replicate w1 False <> P.replicate 4 True <>
                P.replicate w2 False <> P.replicate 4 True <> P.repeat False
  inputWDataM = fromList $ Nothing : P.map Just [1 .. 4] <>
                  P.replicate w3 Nothing <> P.map Just [5 .. 8] <>
                  P.repeat Nothing

-- Test n.1:
   - Write 4
--  - Empty as soon as we can stream all in a row
--  - Stay empty for a moment
   - Write another 4
--  - Empty as soon as we can stream all in a row

sync1R1 :: Signal Slow (Bool, (Int, Bool))
sync1W1 :: Signal System (Maybe Int, Bool)
(sync1R1, sync1W1) = sync12RW 4 2 7

sync2R1 :: Signal System (Bool, (Int, Bool))
sync2W1 :: Signal Slow (Maybe Int, Bool)
(sync2R1, sync2W1) = sync12RW 9 6 1

-- Test n.2:
   - Write 4
--  - Don't start immediately, but then empty all in a row
--  - Stay empty for a moment
   - Write another 4
--  - Empty as soon as we can stream all in a row

sync1R2 :: Signal Slow (Bool, (Int, Bool))
sync1W2 :: Signal System (Maybe Int, Bool)
(sync1R2, sync1W2) = sync12RW 5 2 8

sync2R2 :: Signal System (Bool, (Int, Bool))
sync2W2 :: Signal Slow (Maybe Int, Bool)
(sync2R2, sync2W2) = sync12RW 10 10 1

-- Test n.3:
   - Write 4
--  - Empty as soon as we can stream all in a row
--  - Stay empty for a moment
   - Write another 4
--  - Don't start immediately, but then empty all in a row

sync1R3 :: Signal Slow (Bool, (Int, Bool))
sync1W3 :: Signal System (Maybe Int, Bool)
(sync1R3, sync1W3) = sync12RW 4 4 7

sync2R3 :: Signal System (Bool, (Int, Bool))
sync2W3 :: Signal Slow (Maybe Int, Bool)
(sync2R3, sync2W3) = sync12RW 9 8 1

-- Test n.4:
   - Write 4
--  - Don't start immediately, but then empty all in a row
--  - Stay empty for a moment
   - Write another 4
--  - Don't start immediately, but then empty all in a row

sync1R4 :: Signal Slow (Bool, (Int, Bool))
sync1W4 :: Signal System (Maybe Int, Bool)
(sync1R4, sync1W4) = sync12RW 5 3 8

sync2R4 :: Signal System (Bool, (Int, Bool))
sync2W4 :: Signal Slow (Maybe Int, Bool)
(sync2R4, sync2W4) = sync12RW 10 12 1

sync34RW
  :: ( KnownDomain dom1
     , KnownDomain dom2
     )
  => Int
  -> Int
  -> Int
  -> Int
  -> ( Signal dom1 (Bool, (Int, Bool))
     , Signal dom2 (Maybe Int, Bool)
     )
sync34RW w1 n1 w2 w3 =
  ( bundle (inputRInc, bundle (rdata, rempty))
  , bundle (inputWDataM, wfull)
  )
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d4 clockGen clockGen resetGen resetGen enableGen
      enableGen inputRInc inputWDataM
  inputRInc = fromList $ P.replicate w1 False <> P.replicate n1 True <>
                P.replicate w2 False <> P.replicate 16 True <> P.repeat False
  inputWDataM = fromList $ Nothing : P.map Just [1 .. n1] <>
                  P.replicate w3 Nothing <> P.map Just [n1 + 1 .. n1 + 16] <>
                  P.repeat Nothing

-- Test n.5: Fully fill the FIFO, then empty it again

sync3R5 :: Signal Slow (Bool, (Int, Bool))
sync3W5 :: Signal System (Maybe Int, Bool)
(sync3R5, sync3W5) = sync34RW 0 0 7 0

sync4R5 :: Signal System (Bool, (Int, Bool))
sync4W5 :: Signal Slow (Maybe Int, Bool)
(sync4R5, sync4W5) = sync34RW 0 0 28 0

  Test n.6 : Transfer 3 elements ( so pointers are not zero ) , then fully fill
-- the FIFO, then empty it again

sync3R6 :: Signal Slow (Bool, (Int, Bool))
sync3W6 :: Signal System (Maybe Int, Bool)
(sync3R6, sync3W6) = sync34RW 4 3 4 4

sync4R6 :: Signal System (Bool, (Int, Bool))
sync4W6 :: Signal Slow (Maybe Int, Bool)
(sync4R6, sync4W6) = sync34RW 8 3 25 1

sync56RW
  :: ( KnownDomain dom1
     , KnownDomain dom2
     )
  => ( Signal dom1 (Bool, (Int, Bool))
     , Signal dom2 (Maybe Int, Bool)
     )
sync56RW =
  ( bundle (inputRInc, bundle (rdata, rempty))
  , bundle (inputWDataM, wfull)
  )
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d4 clockGen clockGen resetGen resetGen enableGen
      enableGen inputRInc inputWDataM
  inputRInc = not <$> rempty
  inputWDataM = fromList $ Nothing : P.map Just [1 .. 17] <> P.repeat Nothing

  Test n.7 : Read data as quickly as it comes , all through the memory and one
--           beyond the wrap of the circular buffer

sync5R7 :: Signal Slow (Bool, (Int, Bool))
sync5W7 :: Signal System (Maybe Int, Bool)
(sync5R7, sync5W7) = sync56RW

sync6R7 :: Signal System (Bool, (Int, Bool))
sync6W7 :: Signal Slow (Maybe Int, Bool)
(sync6R7, sync6W7) = sync56RW

-- For use with syncXRY syncXWY.
-- It prints (sample number, (input, output))
printTest
  :: (Foldable f, NFDataX a, ShowX a)
  => f a
  -> IO ()
printTest t = P.mapM_ (putStrLn . showX) $ P.zip [1 :: Int ..] $ sampleN 60 t

sampleR
  :: Foldable f
  => Int
  -> f (Bool, (Int, Bool))
  -> [(Bool, (Maybe Int, Bool))]
sampleR n r =
  P.map (\(inputRInc, (rdata, rempty)) -> (inputRInc, (maybeIsX rdata, rempty)))
    (sampleN n r)

-- For creating the expected constants below
printR
  :: Foldable f
  => f (Bool, (Int, Bool))
  -> IO ()
printR r = case P.map show $ sampleR 60 r of
  (first:rest) ->
    putStrLn $ (P.foldl (\b a -> b <> "  , " <> a <> "\n")
                  ("  [ " <> first <> "\n") rest) <> "  ]"
  _ -> error "impossible"

printW
  :: (Foldable f, NFDataX a, Show a)
  => f a
  -> IO ()
printW w = case P.map show $ sampleN 60 w of
  (first:rest) ->
    putStrLn $ (P.foldl (\b a -> b <> "  , " <> a <> "\n")
                  ("  [ " <> first <> "\n") rest) <> "  ]"
  _ -> error "impossible"

testR
  :: Foldable f
  => f (Bool, (Int, Bool))
  -> [(Bool, (Maybe Int, Bool))]
  -> Assertion
testR r expected = sampleR (P.length expected) r @?= expected

testW
  :: (Foldable f, NFDataX a, Eq a, Show a)
  => f a
  -> [a]
  -> Assertion
testW w expected = sampleN (P.length expected) w @?= expected


test1R1 :: Assertion
test1R1 = testR sync1R1
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,True))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W1 :: Assertion
test1W1 = testW sync1W1
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R1 :: Assertion
test2R1 = testR sync2R1
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W1 :: Assertion
test2W1 = testW sync2W1
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test1R2 :: Assertion
test1R2 = testR sync1R2
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W2 :: Assertion
test1W2 = testW sync1W2
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R2 :: Assertion
test2R2 = testR sync2R2
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W2 :: Assertion
test2W2 = testW sync2W2
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test1R3 :: Assertion
test1R3 = testR sync1R3
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W3 :: Assertion
test1W3 = testW sync1W3
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R3 :: Assertion
test2R3 = testR sync2R3
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W3 :: Assertion
test2W3 = testW sync2W3
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test1R4 :: Assertion
test1R4 = testR sync1R4
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W4 :: Assertion
test1W4 = testW sync1W4
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R4 :: Assertion
test2R4 = testR sync2R4
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W4 :: Assertion
test2W4 = testW sync2W4
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test3R5 :: Assertion
test3R5 = testR sync3R5
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (False,(Just 1,True))
  ]

test3W5 :: Assertion
test3W5 = testW sync3W5
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Nothing,True)
  , (Nothing,False)
  ]

test4R5 :: Assertion
test4R5 = testR sync4R5
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (False,(Just 1,True))
  ]

test4W5 :: Assertion
test4W5 = testW sync4W5
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Nothing,True)
  , (Nothing,False)
  ]

test3R6 :: Assertion
test3R6 = testR sync3R6
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (False,(Just 4,True))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (True,(Just 17,False))
  , (True,(Just 18,False))
  , (True,(Just 19,False))
  , (False,(Just 4,True))
  ]

test3W6 :: Assertion
test3W6 = testW sync3W6
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Just 18,False)
  , (Just 19,False)
  , (Nothing,True)
  , (Nothing,True)
  , (Nothing,False)
  ]

test4R6 :: Assertion
test4R6 = testR sync4R6
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 4,True))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (True,(Just 17,False))
  , (True,(Just 18,False))
  , (True,(Just 19,False))
  , (False,(Just 4,True))
  ]

test4W6 :: Assertion
test4W6 = testW sync4W6
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Nothing,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Just 18,False)
  , (Just 19,False)
  , (Nothing,True)
  , (Nothing,False)
  ]

test5R7 :: Assertion
test5R7 = testR sync5R7
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (True,(Just 17,False))
  , (False,(Just 2,True))
  ]

test5W7 :: Assertion
test5W7 = testW sync5W7
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Nothing,False)
  ]

test6R7 :: Assertion
test6R7 = testR sync6R7
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (False,(Just 2,True))
  , (True,(Just 2,False))
  , (False,(Just 3,True))
  , (True,(Just 3,False))
  , (False,(Just 4,True))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (True,(Just 5,False))
  , (False,(Just 6,True))
  , (True,(Just 6,False))
  , (False,(Just 7,True))
  , (True,(Just 7,False))
  , (False,(Just 8,True))
  , (True,(Just 8,False))
  , (False,(Just 9,True))
  , (True,(Just 9,False))
  , (False,(Just 10,True))
  , (True,(Just 10,False))
  , (False,(Just 11,True))
  , (True,(Just 11,False))
  , (False,(Just 12,True))
  , (True,(Just 12,False))
  , (False,(Just 13,True))
  , (True,(Just 13,False))
  , (False,(Just 14,True))
  , (True,(Just 14,False))
  , (False,(Just 15,True))
  , (True,(Just 15,False))
  , (False,(Just 16,True))
  , (True,(Just 16,False))
  , (False,(Just 17,True))
  , (True,(Just 17,False))
  , (False,(Just 2,True))
  ]

test6W7 :: Assertion
test6W7 = testW sync6W7
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Nothing,False)
  ]

createDomain vSystem{vName="A", vPeriod=hzToPeriod 20e6} -- fast
createDomain vSystem{vName="B", vPeriod=hzToPeriod 10e6} -- slow
createDomain vSystem{vName="C", vPeriod=hzToPeriod 7e6} -- slower

data DomProxy (dom :: Domain) where
  DomProxy
    :: KnownDomain dom
    => DomProxy dom

-- A more useful Show instance than the one for 'Proxy'
instance Show (DomProxy dom) where
  showsPrec d dom@DomProxy =
    showParen (d > app_prec) $ ("DomProxy @" <>) . (symbolVal dom <>)
   where app_prec = 10

data NamedTest a = NamedTest
  { namedTest :: a
  , testName :: String
  }

instance Show (NamedTest a) where
  show nt = testName nt

testNameShowsPrec
  :: Int
  -> ShowS
  -> DomProxy dom1
  -> DomProxy dom2
  -> ShowS
testNameShowsPrec d baseName dom1 dom2 =
  showParen (d > app_prec) $ baseName . (' ':) . showsPrec 11 dom1 .
    (' ':) . showsPrec 11 dom2
 where app_prec = 10

forAllNamedTestProperties
  :: [NamedTest (PropertyT IO ())]
  -> Property

forAllNamedTestProperties nts = property $ do
  t <- fmap namedTest . forAllWith show . Gen.resize 99 $ Gen.element nts
  t

data WriteAction =
    Write
  | WNoOp
  | WGated
  | GatedWrite
  deriving (Eq, Enum, Bounded, Show, Generic, NFDataX)

data ReadAction =
    Read
  | RNoOp
  | RGated
  | GatedRead
  deriving (Eq, Enum, Bounded, Show, Generic, NFDataX)

fifoOperations
  :: forall wdom rdom
   . (KnownDomain wdom, KnownDomain rdom)
  => [ReadAction]
  -> [WriteAction]
  -> ( Signal rdom ( Bool
                     -- Test fully done
                   , Maybe Int
                     -- Element read from FIFO
                   )
     , Signal wdom ( Bool
                     -- Test fully done
                   , Maybe Int
                    -- Element written to FIFO
                   )
     )
fifoOperations racts wacts = (bundle (rAllDone, rel), bundle (wAllDone, wel))
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d3 wclk rclk noWRst noRRst (toEnable wen)
      (toEnable ren) rinc wDataM
  rclk = clockGen @rdom
  wclk = clockGen @wdom
  -- Not resetting makes the test easier to interpret and actual proper testing
  -- of reset behaviour is a lot more involved.
  noRRst = unsafeFromHighPolarity @rdom (pure False)
  noWRst = unsafeFromHighPolarity @wdom (pure False)
  (wdone, wact) =
    unbundle $ fromList $ P.zip (P.repeat False) wacts <> P.repeat (True, WNoOp)
  (rdone, ract) =
    unbundle $ fromList $ P.zip (P.repeat False) racts <> P.repeat (True, RNoOp)
  (wen, wDataM) = unbundle $
    liftA2 (\wact0 d -> case wact0 of
                          Write      -> (True , Just d )
                          WNoOp      -> (True , Nothing)
                          WGated     -> (False, Nothing)
                          GatedWrite -> (False, Just d )
           ) wact wdata
  wdata = regEn wclk noWRst enableGen 1 (isJust <$> wDataM) (wdata + 1)
  (ren, rinc) = unbundle $
    fmap (\ract0 -> case ract0 of
                      Read       -> (True , True )
                      RNoOp      -> (True , False)
                      RGated     -> (False, False)
                      GatedRead  -> (False, True)
         ) ract1
  mainDone = rdone .&&. unsafeSynchronizer wclk rclk wdone
  -- Empty FIFO after main test
  ract1 = mux mainDone (pure Read) ract
  flushCnt = regEn rclk noRRst enableGen (1 :: Int) mainDone (flushCnt + 1)
  Surely in 20 cycles we have seen all of the FIFO even if something has gone
  -- wrong with the pointers
  rAllDone = fmap (> 20) flushCnt
  wAllDone = unsafeSynchronizer rclk wclk rAllDone
  rel = mux (liftA3 (\en inc em -> en && inc && not em) ren rinc rempty)
            (Just <$> rdata) (pure Nothing)
  wel = mux (liftA3 (\en wdm fu -> en && isJust wdm && not fu) wen wDataM wfull)
            (Just <$> wdata) (pure Nothing)

fifoFunctionalTest
  :: forall wdom rdom m
   . Monad m
  => DomProxy wdom
  -> DomProxy rdom
  -> NamedTest (PropertyT m ())
fifoFunctionalTest wdom@DomProxy rdom@DomProxy =
  NamedTest { namedTest=test0, testName = name }
 where
  name = testNameShowsPrec 0 (shows 'fifoFunctionalTest) wdom rdom ""
  genInput :: (Bounded e, Enum e, Show e) => PropertyT m [e]
  genInput = fmap P.concat . forAll . Gen.list (Range.linear 1 10) .
               Gen.list (Range.linear 0 20) $ Gen.enumBounded
  test0 = do
    racts <- genInput
    wacts <- genInput
    let
      results = fifoOperations @wdom @rdom racts wacts
      catValids = catMaybes . P.map snd . P.takeWhile (not . fst)
      rels = catValids . sample $ fst results
      wels = catValids . sample $ snd results
    rels === wels

fifoFunctionalTestCombinations :: Monad m => [NamedTest (PropertyT m ())]
fifoFunctionalTestCombinations =
  [ fifoFunctionalTest (DomProxy @A) (DomProxy @A)
  , fifoFunctionalTest (DomProxy @A) (DomProxy @B)
  , fifoFunctionalTest (DomProxy @A) (DomProxy @C)
  , fifoFunctionalTest (DomProxy @B) (DomProxy @A)
  , fifoFunctionalTest (DomProxy @B) (DomProxy @C)
  , fifoFunctionalTest (DomProxy @C) (DomProxy @A)
  , fifoFunctionalTest (DomProxy @C) (DomProxy @B)
  ]

tests :: TestTree
tests = testGroup "asyncFIFOSynchronizer"
  [ testCase "Test 1.1 Read" test1R1
  , testCase "Test 1.1 Write" test1W1
  , testCase "Test 2.1 Read" test2R1
  , testCase "Test 2.1 Write" test2W1
  , testCase "Test 1.2 Read" test1R2
  , testCase "Test 1.2 Write" test1W2
  , testCase "Test 2.2 Read" test2R2
  , testCase "Test 2.2 Write" test2W2
  , testCase "Test 1.3 Read" test1R3
  , testCase "Test 1.3 Write" test1W3
  , testCase "Test 2.3 Read" test2R3
  , testCase "Test 2.3 Write" test2W3
  , testCase "Test 1.4 Read" test1R4
  , testCase "Test 1.4 Write" test1W4
  , testCase "Test 2.4 Read" test2R4
  , testCase "Test 2.4 Write" test2W4
  , testCase "Test 3.5 Read" test3R5
  , testCase "Test 3.5 Write" test3W5
  , testCase "Test 4.5 Read" test4R5
  , testCase "Test 4.5 Write" test4W5
  , testCase "Test 3.6 Read" test3R6
  , testCase "Test 3.6 Write" test3W6
  , testCase "Test 4.6 Read" test4R6
  , testCase "Test 4.6 Write" test4W6
  , testCase "Test 5.7 Read" test5R7
  , testCase "Test 5.7 Write" test5W7
  , testCase "Test 6.7 Read" test6R7
  , testCase "Test 6.7 Write" test6W7
  , testPropertyXXX "Functional test" $ forAllNamedTestProperties fifoFunctionalTestCombinations
  ]

# LANGUAGE DeriveAnyClass #
# LANGUAGE GADTs #
# OPTIONS_GHC -Wno-unused-top-binds #
 Test n.1:
  - Empty as soon as we can stream all in a row
  - Stay empty for a moment
  - Empty as soon as we can stream all in a row
 Test n.2:
  - Don't start immediately, but then empty all in a row
  - Stay empty for a moment
  - Empty as soon as we can stream all in a row
 Test n.3:
  - Empty as soon as we can stream all in a row
  - Stay empty for a moment
  - Don't start immediately, but then empty all in a row
 Test n.4:
  - Don't start immediately, but then empty all in a row
  - Stay empty for a moment
  - Don't start immediately, but then empty all in a row
 Test n.5: Fully fill the FIFO, then empty it again
 the FIFO, then empty it again
           beyond the wrap of the circular buffer
 For use with syncXRY syncXWY.
 It prints (sample number, (input, output))
 For creating the expected constants below
 fast
 slow
 slower
 A more useful Show instance than the one for 'Proxy'
 Test fully done
 Element read from FIFO
 Test fully done
 Element written to FIFO
 Not resetting makes the test easier to interpret and actual proper testing
 of reset behaviour is a lot more involved.
 Empty FIFO after main test
 wrong with the pointers
# LANGUAGE TemplateHaskell #
# LANGUAGE TypeFamilies #
# OPTIONS_GHC -Wno - orphans #

module Clash.Tests.AsyncFIFOSynchronizer (tests) where

import Data.Maybe (isJust, catMaybes)
import qualified Prelude as P

import Hedgehog as H
import qualified Hedgehog.Range as Range
import qualified Hedgehog.Gen as Gen
import Test.Tasty
import Test.Tasty.Hedgehog.Extra
import Test.Tasty.HUnit

import Clash.Explicit.Prelude

createDomain vSystem{vName = "Slow", vPeriod = 2 * vPeriod vSystem}

sync12RW
  :: ( KnownDomain dom1
     , KnownDomain dom2
     )
  => Int
  -> Int
  -> Int
  -> ( Signal dom1 (Bool, (Int, Bool))
     , Signal dom2 (Maybe Int, Bool)
     )
sync12RW w1 w2 w3 =
  ( bundle (inputRInc, bundle (rdata, rempty))
  , bundle (inputWDataM, wfull)
  )
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d4 clockGen clockGen resetGen resetGen enableGen
      enableGen inputRInc inputWDataM
  inputRInc = fromList $ P.replicate w1 False <> P.replicate 4 True <>
                P.replicate w2 False <> P.replicate 4 True <> P.repeat False
  inputWDataM = fromList $ Nothing : P.map Just [1 .. 4] <>
                  P.replicate w3 Nothing <> P.map Just [5 .. 8] <>
                  P.repeat Nothing

   - Write 4
   - Write another 4

sync1R1 :: Signal Slow (Bool, (Int, Bool))
sync1W1 :: Signal System (Maybe Int, Bool)
(sync1R1, sync1W1) = sync12RW 4 2 7

sync2R1 :: Signal System (Bool, (Int, Bool))
sync2W1 :: Signal Slow (Maybe Int, Bool)
(sync2R1, sync2W1) = sync12RW 9 6 1

   - Write 4
   - Write another 4

sync1R2 :: Signal Slow (Bool, (Int, Bool))
sync1W2 :: Signal System (Maybe Int, Bool)
(sync1R2, sync1W2) = sync12RW 5 2 8

sync2R2 :: Signal System (Bool, (Int, Bool))
sync2W2 :: Signal Slow (Maybe Int, Bool)
(sync2R2, sync2W2) = sync12RW 10 10 1

   - Write 4
   - Write another 4

sync1R3 :: Signal Slow (Bool, (Int, Bool))
sync1W3 :: Signal System (Maybe Int, Bool)
(sync1R3, sync1W3) = sync12RW 4 4 7

sync2R3 :: Signal System (Bool, (Int, Bool))
sync2W3 :: Signal Slow (Maybe Int, Bool)
(sync2R3, sync2W3) = sync12RW 9 8 1

   - Write 4
   - Write another 4

sync1R4 :: Signal Slow (Bool, (Int, Bool))
sync1W4 :: Signal System (Maybe Int, Bool)
(sync1R4, sync1W4) = sync12RW 5 3 8

sync2R4 :: Signal System (Bool, (Int, Bool))
sync2W4 :: Signal Slow (Maybe Int, Bool)
(sync2R4, sync2W4) = sync12RW 10 12 1

sync34RW
  :: ( KnownDomain dom1
     , KnownDomain dom2
     )
  => Int
  -> Int
  -> Int
  -> Int
  -> ( Signal dom1 (Bool, (Int, Bool))
     , Signal dom2 (Maybe Int, Bool)
     )
sync34RW w1 n1 w2 w3 =
  ( bundle (inputRInc, bundle (rdata, rempty))
  , bundle (inputWDataM, wfull)
  )
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d4 clockGen clockGen resetGen resetGen enableGen
      enableGen inputRInc inputWDataM
  inputRInc = fromList $ P.replicate w1 False <> P.replicate n1 True <>
                P.replicate w2 False <> P.replicate 16 True <> P.repeat False
  inputWDataM = fromList $ Nothing : P.map Just [1 .. n1] <>
                  P.replicate w3 Nothing <> P.map Just [n1 + 1 .. n1 + 16] <>
                  P.repeat Nothing


sync3R5 :: Signal Slow (Bool, (Int, Bool))
sync3W5 :: Signal System (Maybe Int, Bool)
(sync3R5, sync3W5) = sync34RW 0 0 7 0

sync4R5 :: Signal System (Bool, (Int, Bool))
sync4W5 :: Signal Slow (Maybe Int, Bool)
(sync4R5, sync4W5) = sync34RW 0 0 28 0

  Test n.6 : Transfer 3 elements ( so pointers are not zero ) , then fully fill

sync3R6 :: Signal Slow (Bool, (Int, Bool))
sync3W6 :: Signal System (Maybe Int, Bool)
(sync3R6, sync3W6) = sync34RW 4 3 4 4

sync4R6 :: Signal System (Bool, (Int, Bool))
sync4W6 :: Signal Slow (Maybe Int, Bool)
(sync4R6, sync4W6) = sync34RW 8 3 25 1

sync56RW
  :: ( KnownDomain dom1
     , KnownDomain dom2
     )
  => ( Signal dom1 (Bool, (Int, Bool))
     , Signal dom2 (Maybe Int, Bool)
     )
sync56RW =
  ( bundle (inputRInc, bundle (rdata, rempty))
  , bundle (inputWDataM, wfull)
  )
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d4 clockGen clockGen resetGen resetGen enableGen
      enableGen inputRInc inputWDataM
  inputRInc = not <$> rempty
  inputWDataM = fromList $ Nothing : P.map Just [1 .. 17] <> P.repeat Nothing

  Test n.7 : Read data as quickly as it comes , all through the memory and one

sync5R7 :: Signal Slow (Bool, (Int, Bool))
sync5W7 :: Signal System (Maybe Int, Bool)
(sync5R7, sync5W7) = sync56RW

sync6R7 :: Signal System (Bool, (Int, Bool))
sync6W7 :: Signal Slow (Maybe Int, Bool)
(sync6R7, sync6W7) = sync56RW

printTest
  :: (Foldable f, NFDataX a, ShowX a)
  => f a
  -> IO ()
printTest t = P.mapM_ (putStrLn . showX) $ P.zip [1 :: Int ..] $ sampleN 60 t

sampleR
  :: Foldable f
  => Int
  -> f (Bool, (Int, Bool))
  -> [(Bool, (Maybe Int, Bool))]
sampleR n r =
  P.map (\(inputRInc, (rdata, rempty)) -> (inputRInc, (maybeIsX rdata, rempty)))
    (sampleN n r)

printR
  :: Foldable f
  => f (Bool, (Int, Bool))
  -> IO ()
printR r = case P.map show $ sampleR 60 r of
  (first:rest) ->
    putStrLn $ (P.foldl (\b a -> b <> "  , " <> a <> "\n")
                  ("  [ " <> first <> "\n") rest) <> "  ]"
  _ -> error "impossible"

printW
  :: (Foldable f, NFDataX a, Show a)
  => f a
  -> IO ()
printW w = case P.map show $ sampleN 60 w of
  (first:rest) ->
    putStrLn $ (P.foldl (\b a -> b <> "  , " <> a <> "\n")
                  ("  [ " <> first <> "\n") rest) <> "  ]"
  _ -> error "impossible"

testR
  :: Foldable f
  => f (Bool, (Int, Bool))
  -> [(Bool, (Maybe Int, Bool))]
  -> Assertion
testR r expected = sampleR (P.length expected) r @?= expected

testW
  :: (Foldable f, NFDataX a, Eq a, Show a)
  => f a
  -> [a]
  -> Assertion
testW w expected = sampleN (P.length expected) w @?= expected


test1R1 :: Assertion
test1R1 = testR sync1R1
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,True))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W1 :: Assertion
test1W1 = testW sync1W1
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R1 :: Assertion
test2R1 = testR sync2R1
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W1 :: Assertion
test2W1 = testW sync2W1
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test1R2 :: Assertion
test1R2 = testR sync1R2
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W2 :: Assertion
test1W2 = testW sync1W2
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R2 :: Assertion
test2R2 = testR sync2R2
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W2 :: Assertion
test2W2 = testW sync2W2
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test1R3 :: Assertion
test1R3 = testR sync1R3
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W3 :: Assertion
test1W3 = testW sync1W3
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R3 :: Assertion
test2R3 = testR sync2R3
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W3 :: Assertion
test2W3 = testW sync2W3
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test1R4 :: Assertion
test1R4 = testR sync1R4
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test1W4 :: Assertion
test1W4 = testW sync1W4
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test2R4 :: Assertion
test2R4 = testR sync2R4
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (False,(Nothing,True))
  , (False,(Just 5,True))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (False,(Just 5,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (False,(Nothing,True))
  ]

test2W4 :: Assertion
test2W4 = testW sync2W4
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Nothing,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Nothing,False)
  ]

test3R5 :: Assertion
test3R5 = testR sync3R5
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (False,(Just 1,True))
  ]

test3W5 :: Assertion
test3W5 = testW sync3W5
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Nothing,True)
  , (Nothing,False)
  ]

test4R5 :: Assertion
test4R5 = testR sync4R5
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (False,(Just 1,True))
  ]

test4W5 :: Assertion
test4W5 = testW sync4W5
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Nothing,True)
  , (Nothing,False)
  ]

test3R6 :: Assertion
test3R6 = testR sync3R6
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (False,(Just 4,True))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (True,(Just 17,False))
  , (True,(Just 18,False))
  , (True,(Just 19,False))
  , (False,(Just 4,True))
  ]

test3W6 :: Assertion
test3W6 = testW sync3W6
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Nothing,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Just 18,False)
  , (Just 19,False)
  , (Nothing,True)
  , (Nothing,True)
  , (Nothing,False)
  ]

test4R6 :: Assertion
test4R6 = testR sync4R6
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,False))
  , (False,(Just 1,False))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 4,True))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (False,(Just 4,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (True,(Just 17,False))
  , (True,(Just 18,False))
  , (True,(Just 19,False))
  , (False,(Just 4,True))
  ]

test4W6 :: Assertion
test4W6 = testW sync4W6
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Nothing,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Just 18,False)
  , (Just 19,False)
  , (Nothing,True)
  , (Nothing,False)
  ]

test5R7 :: Assertion
test5R7 = testR sync5R7
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (True,(Just 2,False))
  , (True,(Just 3,False))
  , (True,(Just 4,False))
  , (True,(Just 5,False))
  , (True,(Just 6,False))
  , (True,(Just 7,False))
  , (True,(Just 8,False))
  , (True,(Just 9,False))
  , (True,(Just 10,False))
  , (True,(Just 11,False))
  , (True,(Just 12,False))
  , (True,(Just 13,False))
  , (True,(Just 14,False))
  , (True,(Just 15,False))
  , (True,(Just 16,False))
  , (True,(Just 17,False))
  , (False,(Just 2,True))
  ]

test5W7 :: Assertion
test5W7 = testW sync5W7
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Nothing,False)
  ]

test6R7 :: Assertion
test6R7 = testR sync6R7
  [ (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Nothing,True))
  , (False,(Just 1,True))
  , (True,(Just 1,False))
  , (False,(Just 2,True))
  , (True,(Just 2,False))
  , (False,(Just 3,True))
  , (True,(Just 3,False))
  , (False,(Just 4,True))
  , (True,(Just 4,False))
  , (False,(Just 5,True))
  , (True,(Just 5,False))
  , (False,(Just 6,True))
  , (True,(Just 6,False))
  , (False,(Just 7,True))
  , (True,(Just 7,False))
  , (False,(Just 8,True))
  , (True,(Just 8,False))
  , (False,(Just 9,True))
  , (True,(Just 9,False))
  , (False,(Just 10,True))
  , (True,(Just 10,False))
  , (False,(Just 11,True))
  , (True,(Just 11,False))
  , (False,(Just 12,True))
  , (True,(Just 12,False))
  , (False,(Just 13,True))
  , (True,(Just 13,False))
  , (False,(Just 14,True))
  , (True,(Just 14,False))
  , (False,(Just 15,True))
  , (True,(Just 15,False))
  , (False,(Just 16,True))
  , (True,(Just 16,False))
  , (False,(Just 17,True))
  , (True,(Just 17,False))
  , (False,(Just 2,True))
  ]

test6W7 :: Assertion
test6W7 = testW sync6W7
  [ (Nothing,False)
  , (Just 1,False)
  , (Just 2,False)
  , (Just 3,False)
  , (Just 4,False)
  , (Just 5,False)
  , (Just 6,False)
  , (Just 7,False)
  , (Just 8,False)
  , (Just 9,False)
  , (Just 10,False)
  , (Just 11,False)
  , (Just 12,False)
  , (Just 13,False)
  , (Just 14,False)
  , (Just 15,False)
  , (Just 16,False)
  , (Just 17,False)
  , (Nothing,False)
  ]


data DomProxy (dom :: Domain) where
  DomProxy
    :: KnownDomain dom
    => DomProxy dom

instance Show (DomProxy dom) where
  showsPrec d dom@DomProxy =
    showParen (d > app_prec) $ ("DomProxy @" <>) . (symbolVal dom <>)
   where app_prec = 10

data NamedTest a = NamedTest
  { namedTest :: a
  , testName :: String
  }

instance Show (NamedTest a) where
  show nt = testName nt

testNameShowsPrec
  :: Int
  -> ShowS
  -> DomProxy dom1
  -> DomProxy dom2
  -> ShowS
testNameShowsPrec d baseName dom1 dom2 =
  showParen (d > app_prec) $ baseName . (' ':) . showsPrec 11 dom1 .
    (' ':) . showsPrec 11 dom2
 where app_prec = 10

forAllNamedTestProperties
  :: [NamedTest (PropertyT IO ())]
  -> Property

forAllNamedTestProperties nts = property $ do
  t <- fmap namedTest . forAllWith show . Gen.resize 99 $ Gen.element nts
  t

data WriteAction =
    Write
  | WNoOp
  | WGated
  | GatedWrite
  deriving (Eq, Enum, Bounded, Show, Generic, NFDataX)

data ReadAction =
    Read
  | RNoOp
  | RGated
  | GatedRead
  deriving (Eq, Enum, Bounded, Show, Generic, NFDataX)

fifoOperations
  :: forall wdom rdom
   . (KnownDomain wdom, KnownDomain rdom)
  => [ReadAction]
  -> [WriteAction]
  -> ( Signal rdom ( Bool
                   , Maybe Int
                   )
     , Signal wdom ( Bool
                   , Maybe Int
                   )
     )
fifoOperations racts wacts = (bundle (rAllDone, rel), bundle (wAllDone, wel))
 where
  (rdata, rempty, wfull) =
    asyncFIFOSynchronizer d3 wclk rclk noWRst noRRst (toEnable wen)
      (toEnable ren) rinc wDataM
  rclk = clockGen @rdom
  wclk = clockGen @wdom
  noRRst = unsafeFromHighPolarity @rdom (pure False)
  noWRst = unsafeFromHighPolarity @wdom (pure False)
  (wdone, wact) =
    unbundle $ fromList $ P.zip (P.repeat False) wacts <> P.repeat (True, WNoOp)
  (rdone, ract) =
    unbundle $ fromList $ P.zip (P.repeat False) racts <> P.repeat (True, RNoOp)
  (wen, wDataM) = unbundle $
    liftA2 (\wact0 d -> case wact0 of
                          Write      -> (True , Just d )
                          WNoOp      -> (True , Nothing)
                          WGated     -> (False, Nothing)
                          GatedWrite -> (False, Just d )
           ) wact wdata
  wdata = regEn wclk noWRst enableGen 1 (isJust <$> wDataM) (wdata + 1)
  (ren, rinc) = unbundle $
    fmap (\ract0 -> case ract0 of
                      Read       -> (True , True )
                      RNoOp      -> (True , False)
                      RGated     -> (False, False)
                      GatedRead  -> (False, True)
         ) ract1
  mainDone = rdone .&&. unsafeSynchronizer wclk rclk wdone
  ract1 = mux mainDone (pure Read) ract
  flushCnt = regEn rclk noRRst enableGen (1 :: Int) mainDone (flushCnt + 1)
  Surely in 20 cycles we have seen all of the FIFO even if something has gone
  rAllDone = fmap (> 20) flushCnt
  wAllDone = unsafeSynchronizer rclk wclk rAllDone
  rel = mux (liftA3 (\en inc em -> en && inc && not em) ren rinc rempty)
            (Just <$> rdata) (pure Nothing)
  wel = mux (liftA3 (\en wdm fu -> en && isJust wdm && not fu) wen wDataM wfull)
            (Just <$> wdata) (pure Nothing)

fifoFunctionalTest
  :: forall wdom rdom m
   . Monad m
  => DomProxy wdom
  -> DomProxy rdom
  -> NamedTest (PropertyT m ())
fifoFunctionalTest wdom@DomProxy rdom@DomProxy =
  NamedTest { namedTest=test0, testName = name }
 where
  name = testNameShowsPrec 0 (shows 'fifoFunctionalTest) wdom rdom ""
  genInput :: (Bounded e, Enum e, Show e) => PropertyT m [e]
  genInput = fmap P.concat . forAll . Gen.list (Range.linear 1 10) .
               Gen.list (Range.linear 0 20) $ Gen.enumBounded
  test0 = do
    racts <- genInput
    wacts <- genInput
    let
      results = fifoOperations @wdom @rdom racts wacts
      catValids = catMaybes . P.map snd . P.takeWhile (not . fst)
      rels = catValids . sample $ fst results
      wels = catValids . sample $ snd results
    rels === wels

fifoFunctionalTestCombinations :: Monad m => [NamedTest (PropertyT m ())]
fifoFunctionalTestCombinations =
  [ fifoFunctionalTest (DomProxy @A) (DomProxy @A)
  , fifoFunctionalTest (DomProxy @A) (DomProxy @B)
  , fifoFunctionalTest (DomProxy @A) (DomProxy @C)
  , fifoFunctionalTest (DomProxy @B) (DomProxy @A)
  , fifoFunctionalTest (DomProxy @B) (DomProxy @C)
  , fifoFunctionalTest (DomProxy @C) (DomProxy @A)
  , fifoFunctionalTest (DomProxy @C) (DomProxy @B)
  ]

tests :: TestTree
tests = testGroup "asyncFIFOSynchronizer"
  [ testCase "Test 1.1 Read" test1R1
  , testCase "Test 1.1 Write" test1W1
  , testCase "Test 2.1 Read" test2R1
  , testCase "Test 2.1 Write" test2W1
  , testCase "Test 1.2 Read" test1R2
  , testCase "Test 1.2 Write" test1W2
  , testCase "Test 2.2 Read" test2R2
  , testCase "Test 2.2 Write" test2W2
  , testCase "Test 1.3 Read" test1R3
  , testCase "Test 1.3 Write" test1W3
  , testCase "Test 2.3 Read" test2R3
  , testCase "Test 2.3 Write" test2W3
  , testCase "Test 1.4 Read" test1R4
  , testCase "Test 1.4 Write" test1W4
  , testCase "Test 2.4 Read" test2R4
  , testCase "Test 2.4 Write" test2W4
  , testCase "Test 3.5 Read" test3R5
  , testCase "Test 3.5 Write" test3W5
  , testCase "Test 4.5 Read" test4R5
  , testCase "Test 4.5 Write" test4W5
  , testCase "Test 3.6 Read" test3R6
  , testCase "Test 3.6 Write" test3W6
  , testCase "Test 4.6 Read" test4R6
  , testCase "Test 4.6 Write" test4W6
  , testCase "Test 5.7 Read" test5R7
  , testCase "Test 5.7 Write" test5W7
  , testCase "Test 6.7 Read" test6R7
  , testCase "Test 6.7 Write" test6W7
  , testPropertyXXX "Functional test" $ forAllNamedTestProperties fifoFunctionalTestCombinations
  ]
