packages feed

df1-html-0.1: test/Main.hs

{-# LANGUAGE OverloadedLists #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

import qualified Data.Sequence as Seq
import Data.String (fromString)
import qualified Data.Text.Lazy as TL
import qualified Data.Time.Clock.System as Time
import qualified Df1 as D
import qualified Df1.Html.Parse as DHP
import qualified Df1.Html.Render as DHR
import qualified Test.Tasty as Tasty
import qualified Test.Tasty.HUnit as HU
import Test.Tasty.HUnit ((@=?))
import Test.Tasty.QuickCheck ((===))
import qualified Test.Tasty.QuickCheck as QC
import qualified Test.Tasty.Runners as Tasty
import qualified Xmlbf as X

--------------------------------------------------------------------------------

main :: IO ()
main =
  Tasty.defaultMainWithIngredients
    [ Tasty.consoleTestReporter,
      Tasty.listingTests
    ]
    tt

tt :: Tasty.TestTree
tt =
  Tasty.testGroup
    "df1-html"
    [ HU.testCase "Given a D.Log, render it as HTML" $ do
        expected1 @=? DHR.log log1,

      HU.testCase "Parse that HTML to reobtain the D.Log" $ do 
         case X.runParser DHP.log expected1 of 
           Left _ -> fail "Could not parse Log."
           Right a -> log1 @=? a,
           
      Tasty.localOption (QC.QuickCheckTests 2000) $
      QC.testProperty "Render/Parse roundtrip" $ do
        QC.forAllShrink QC.arbitrary QC.shrink $ \log0 -> do
         let html = DHR.log log0
         Right log0 === X.runParser DHP.log html 
    ]


log1 :: D.Log
log1 =
  D.Log
    { D.log_time = Time.MkSystemTime 355 43,
      D.log_level = D.Warning,
      D.log_path = examplePath,
      D.log_message = D.message ("example" :: String)
    }

examplePath :: Seq.Seq D.Path
examplePath =
  [ D.Push (D.segment ("foo" :: String)),
    D.Attr (D.key ("=" :: String)) (D.value ("a" :: String)),
    D.Attr (D.key ("y" :: String)) (D.value ("b" :: String)),
    D.Push (D.segment ("bar" :: String)),
    D.Push (D.segment ("qux" :: String)),
    D.Attr (D.key ("z" :: String)) (D.value ("c" :: String)),
    D.Attr (D.key ("z" :: String)) (D.value ("d" :: String))
  ]


expected1 :: [X.Node]
expected1 =
  X.element
    "div"
    [("class", "df1-log df1-warning")]
    $ mconcat
      [ X.element "span" [("class", "df1-time")] (X.text "1970-01-01T00:05:55.000000043Z"),
        X.text " ",
        X.element "span" [("class", "df1-path")] $
          mconcat
            [ X.element "span" [("class", "df1-push")] $ mconcat [X.text "/", X.element "span" [("class", "df1-seg")] (X.text "foo")],
              X.text " ",
              X.element "span" [("class", "df1-attr")] $
                mconcat
                  [ X.element "span" [("class", "df1-key")] (X.text "%3d"),
                    X.text "=",
                    X.element "span" [("class", "df1-value")] (X.text "a")
                  ],
              X.text " ",
              X.element "span" [("class", "df1-attr")] $
                mconcat
                  [ X.element "span" [("class", "df1-key")] (X.text "y"),
                    X.text "=",
                    X.element "span" [("class", "df1-value")] (X.text "b")
                  ],
              X.text " ",
              X.element "span" [("class", "df1-push")] $ mconcat [X.text "/", X.element "span" [("class", "df1-seg")] (X.text "bar")],
              X.text " ",
              X.element "span" [("class", "df1-push")] $ mconcat [X.text "/", X.element "span" [("class", "df1-seg")] (X.text "qux")],
              X.text " ",
              X.element "span" [("class", "df1-attr")] $
                mconcat
                  [ X.element "span" [("class", "df1-key")] (X.text "z"),
                    X.text "=",
                    X.element "span" [("class", "df1-value")] (X.text "c")
                  ],
              X.text " ",
              X.element "span" [("class", "df1-attr")] $
                mconcat
                  [ X.element "span" [("class", "df1-key")] (X.text "z"),
                    X.text "=",
                    X.element "span" [("class", "df1-value")] (X.text "d")
                  ]
            ],
        X.text " ",
        X.element "span" [("class", "df1-level")] (X.text "WARNING"),
        X.text " ",
        X.element "span" [("class", "df1-msg")] (X.text "example")
      ]


instance QC.Arbitrary D.Log where
  arbitrary = D.Log
    <$> genSystemTime <*> QC.arbitrary <*> QC.arbitrary <*> QC.arbitrary
  shrink (D.Log a b c d) = D.Log
    <$> pure a <*> QC.shrink b <*> QC.shrink c <*> QC.shrink d

instance QC.Arbitrary D.Path where
  arbitrary = QC.oneof
    [ D.Push <$> QC.arbitrary
    , D.Attr <$> QC.arbitrary <*> QC.arbitrary ]
  shrink (D.Push s) = D.Push <$> QC.shrink s
  shrink (D.Attr k v) = D.Attr <$> QC.shrink k <*> QC.shrink v

instance QC.Arbitrary D.Level where
  arbitrary = QC.elements [minBound .. minBound]

instance QC.Arbitrary D.Segment where
  arbitrary = fromString <$> QC.arbitrary
  shrink = map fromString . QC.shrink . TL.unpack . D.unSegment

instance QC.Arbitrary D.Key where
  arbitrary = fromString <$> QC.arbitrary
  shrink = map fromString . QC.shrink . TL.unpack . D.unKey

instance QC.Arbitrary D.Value where
  arbitrary = fromString <$> QC.arbitrary
  shrink = map fromString . QC.shrink . TL.unpack . D.unValue

instance QC.Arbitrary D.Message where
  arbitrary = fromString <$> QC.arbitrary
  shrink = map fromString . QC.shrink . TL.unpack . D.unMessage

genSystemTime :: QC.Gen Time.SystemTime
genSystemTime = do
  a <- QC.choose (0, 253402300799) -- up to 4 digit years
  b <- QC.choose (0, 1000000000)
  pure (Time.MkSystemTime a b)