packages feed

telescope-0.2.0: test/Test/Asdf/ClassSpec.hs

module Test.Asdf.ClassSpec where

import Conduit
import Data.ByteString (ByteString)
import Data.Massiv.Array (Array, Comp (..), D, Ix1, P)
import Data.Massiv.Array qualified as M
import Data.Text (Text)
import Effectful.Resource
import GHC.Generics (Generic, from)
import GHC.Int (Int32, Int64)
import Skeletest
import System.ByteOrder
import Telescope.Asdf.Class
import Telescope.Asdf.Encoding
import Telescope.Asdf.Encoding.File
import Telescope.Asdf.Error
import Telescope.Asdf.NDArray
import Telescope.Asdf.Node
import Telescope.Data.Axes
import Telescope.Data.Parser
import Test.Asdf.DecodeSpec (ExampleTreeFix (..), parseIO)
import Text.Libyaml qualified as Yaml


spec :: Spec
spec = do
  describe "FromAsdf" fromAsdfSpec
  describe "ToAsdf" toAsdfSpec
  describe "GObject" gObjectSpec


fromAsdfSpec :: Spec
fromAsdfSpec = do
  it "parses object" $ do
    let (tree :: Object) =
          [ ("foo", fromValue $ Integer 42)
          , ("name", fromValue $ String "Monty")
          , ("sequence", fromValue $ Array $ fmap (fromValue . Integer) [0 .. 99])
          , ("random", fromValue $ NDArray $ NDArrayData "" BigEndian Float64 (axesRowMajor [0]))
          ]
    ex <- parseIO $ parseValue @Example (Object tree)
    ex.foo `shouldBe` 42
    ex.name `shouldBe` "Monty"
    ex.sequence `shouldBe` [0 .. 99]

  it "parses sequence from example.asdf as an NDArray" $ do
    ExampleTreeFix (Tree tree) <- getFixture
    (Sequence s) <- parseIO $ parseValue @Sequence (Object tree)
    s `shouldBe` M.delay (M.fromLists' @P Seq [0 .. 99])

  it "resolves anchors" $ do
    let ex = AnchorExample "world" "asdf"
    out <- runAsdfM $ encode ex
    ex2 <- runAsdfM $ decode @AnchorExample out
    ex2.message `shouldBe` "world"
    ex2.alias `shouldBe` "world"


toAsdfSpec :: Spec
toAsdfSpec = do
  it "should serialize Example" $ do
    let ex = Example{foo = 40, name = "Marty", sequence = [], powers = Nothing, random = M.empty}
    let Node s anc val = toNode ex
    s `shouldBe` schema ex
    anc `shouldBe` Just "example"
    o <- expectObject val
    lookup "foo" o `shouldBe` Just (Node mempty Nothing (Integer 40))
    lookup "name" o `shouldBe` Just (Node mempty Nothing (String "Marty"))

  it "should serialize Example sequence as list" $ do
    let val = toValue $ Example{foo = 40, name = "Marty", sequence = [0 .. 99], powers = Nothing, random = M.empty}
    o <- expectObject val
    ns <- expectArray $ lookup "sequence" o
    ns `shouldBe` fmap (fromValue . Integer) [0 .. 99]

  it "should serialize list to Array" $ do
    let nums = [0 .. 99] :: [Int]
    toValue nums `shouldBe` Array (fmap (Node mempty Nothing . Integer) [0 .. 99])

  it "should forward schema to maybes" $ do
    schema @(Maybe Example) Nothing `shouldBe` mempty
    schema @(Maybe Example) (Just undefined) `shouldBe` schema @Example undefined



-- it "should produce similar example.asdf" $ do
--   ExampleFileFix inp _ <- getFixture
--   e <- decodeM @Example inp
--   -- o <- encodeM e
--   -- BS.writeFile "dump/test.asdf" o
--   -- print e
--   fail ":NOPE"

gObjectSpec :: Spec
gObjectSpec = do
  it "should gen object" $ do
    gToObject (from (TinyGen "world")) `shouldBe` [("hello", fromValue (String "world"))]

  it "should gen tiny type" $ do
    toValue (TinyGen "world") `shouldBe` Object [("hello", fromValue (String "world"))]

  it "should gen maybe type" $ do
    toValue (MaybeGen (Just "world")) `shouldBe` Object [("hello", fromValue (String "world"))]
    toValue (MaybeGen Nothing) `shouldBe` Object [("hello", fromValue Null)]

  it "should allow maybes" $ do
    let val = Object [("hello", fromValue (String "world"))]
    m1 <- parseIO (parseValue val)
    m1 `shouldBe` MaybeGen (Just "world")

    m2 <- parseIO (parseValue $ Object [])
    m2 `shouldBe` MaybeGen Nothing


-- newtype ExampleAsdfFix = ExampleAsdfFix Asdf
-- instance Fixture ExampleAsdfFix where
--   fixtureAction = do
--     ExampleFileFix _ f <- getFixture
--     a <- runAsdfM $ fromAsdfFile f.tree f.blocks
--     pure $ noCleanup $ ExampleAsdfFix a

dumpEvents :: ByteString -> IO ()
dumpEvents inp = do
  runAsdfM $ do
    a <- splitAsdfFile inp
    runResource $ runConduit $ Yaml.decode a.tree.bytes .| takeC 100 .| mapM_C (liftIO . print)


data TinyGen = TinyGen
  { hello :: Text
  }
  deriving (Generic, FromAsdf, ToAsdf)


data MaybeGen = MaybeGen
  { hello :: Maybe Text
  }
  deriving (Generic, FromAsdf, ToAsdf, Eq)


data Example = Example
  { foo :: Int32
  , name :: Text
  , powers :: Maybe Powers
  , sequence :: [Int64]
  , random :: Array D Ix1 Double
  }
  deriving (Generic, Show)
instance ToAsdf Example where
  schema _ = "example/woot-1.0"
  anchor _ = Just "example"
instance FromAsdf Example where
  parseValue = \case
    Object o -> do
      foo <- o .: "foo"
      name <- o .: "name"
      sq <- o .: "sequence" >>= parseSequence
      powers <- o .:? "powers"
      random <- o .: "random"
      pure $ Example{foo, name, sequence = sq, powers, random}
    val -> expected "Example" val
   where
    parseSequence = \case
      Array ns -> mapM parseNode ns
      NDArray nd -> fromNDArray nd
      val -> expected "Array or NDArray" val


data Powers = Powers
  {squares :: Array D Ix1 Int64}
  deriving (Generic, Show, ToAsdf, FromAsdf)


data Sequence = Sequence (Array D Ix1 Int64)
  deriving (Eq)
instance FromAsdf Sequence where
  parseValue = \case
    Object o -> do
      sql <- o .: "sequence"
      pure $ Sequence sql
    node -> expected "Example Sequence" node


data AnchorExample = AnchorExample
  { message :: Text
  , alias :: Text
  }
  deriving (Generic, FromAsdf)
instance ToAsdf AnchorExample where
  toValue ae =
    Object
      [ ("message", Node mempty (Just "message") $ String ae.message)
      , ("alias", toNode $ Alias "message")
      ]


expectArray :: Maybe Node -> IO [Node]
expectArray = \case
  Just (Node _ _ (Array ns)) -> pure ns
  n -> fail $ "Expected Array, but got: " ++ show n


expectObject :: Value -> IO Object
expectObject = \case
  Object o -> pure o
  n -> fail $ "Expected Object, but got: " ++ show n