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