telescope-0.2.0: test/Test/Asdf/FileSpec.hs
module Test.Asdf.FileSpec where
import Data.Binary.Get
import Data.Binary.Put
import Data.ByteString (ByteString)
import Data.ByteString qualified as BS
import Data.ByteString.Lazy qualified as BL
import Effectful
import Effectful.NonDet
import Effectful.State.Static.Local
import Skeletest
import Skeletest.Predicate ((>>>))
import Skeletest.Predicate qualified as P
import Telescope.Asdf.Encoding.File
import Telescope.Asdf.Error
spec :: Spec
spec = do
describe "blocks" testBlocks
describe "split-file" testSplit
testBlocks :: Spec
testBlocks = do
let blockHeadZero = BlockHeader 1 0 NoCompression 0 0 0 noChecksum
describe "BlockHeader" $ do
it "should parse zero" $ do
let bh = runPut (putBlockHeader blockHeadZero)
goGet getBlockHeader bh `shouldBe` Right blockHeadZero
it "should create from data" $ do
let block = BlockData "hello"
let bh = blockHeader block
bh.allocatedSize `shouldBe` 5
bh.usedSize `shouldBe` 5
bh.allocatedSize `shouldBe` 5
bh.usedSize `shouldBe` 5
bh.dataSize `shouldBe` 5
bh.headerSize `shouldSatisfy` P.gte 48
describe "putBlock" $ do
it "puts a header" $ do
runPut (putBlockHeader blockHeadZero) `shouldSatisfy` (BL.length >>> P.gt 10)
it "puts a block" $ do
runPut (putBlock $ BlockData "hello") `shouldSatisfy` (BL.length >>> P.gt 10)
describe "getBlock" $ do
it "fails on empty data" $ do
goGet getBlock "" `shouldSatisfy` P.left P.anything
it "fails if missing magic byte" $ do
goGet getBlock "asdf" `shouldSatisfy` P.left P.anything
it "puts an empty block" $ do
let out = runPut (putBlock $ BlockData "")
goGet getBlock out `shouldBe` Right (BlockData "")
describe "getBlocks" $ do
it "looks ahead without failing" $ do
goGet checkMagicToken "asdf" `shouldBe` Right False
it "gets a single block" $ do
let out = runPut (putBlock $ BlockData "")
goGet getBlocks out `shouldBe` Right [BlockData ""]
it "gets empty array" $ do
goGet getBlocks "" `shouldBe` Right []
where
goGet a bs =
case runGetOrFail a bs of
Left e -> Left e
Right (_, _, x) -> Right x
testSplit :: Spec
testSplit = do
describe "parse tree" $ do
it "empty" $ do
t <- runParse "" parseTree
t `shouldBe` Encoded ""
it "tree only" $ do
(t, rest) <- runParse "value" $ do
t <- parseTree
rest <- get @ByteString
pure (t, rest)
t `shouldBe` Encoded "value"
rest `shouldBe` ""
it "tree with data" $ do
let Encoded bks = encodeBlock (BlockData "woot")
let inp = "value\n" <> bks
t <- runParse inp parseTree
t `shouldBe` Encoded "value\n"
it "tree with index" $ do
let Encoded ix = encodeIndex (BlockIndex [4])
let inp = "value\n" <> ix
(t, rest) <- runParse inp $ do
t <- parseTree
rest <- get @ByteString
pure (t, rest)
t `shouldBe` Encoded "value\n"
rest `shouldBe` ix
it "tree with all" $ do
let Encoded bks = encodeBlock (BlockData "woot")
let Encoded ix = encodeIndex (BlockIndex [4])
let inp = "value\n" <> bks <> ix
t <- runParse inp parseTree
t `shouldBe` Encoded "value\n"
describe "parse blocks" $ do
it "one block" $ do
let Encoded blks = encodeBlock (BlockData "woot")
n <- runParse blks parseBlock
n `shouldBe` Encoded blks
it "first block" $ do
let Encoded b1 = encodeBlock (BlockData "b1")
let Encoded b2 = encodeBlock (BlockData "b2")
b <- runParse (b1 <> b2) parseBlock
b `shouldBe` Encoded b1
it "block with index" $ do
let Encoded b1 = encodeBlock (BlockData "b1")
let Encoded ix = encodeIndex (BlockIndex [10])
let inp = b1 <> ix
n <- runParse inp parseBlock
n `shouldBe` Encoded b1
it "blocks" $ do
let Encoded b1 = encodeBlock (BlockData "b1")
let Encoded b2 = encodeBlock (BlockData "b2")
let inp = b1 <> b2
bs <- runParse inp parseBlocks
bs `shouldBe` [Encoded b1, Encoded b2]
it "blocks and index" $ do
let Encoded b1 = encodeBlock (BlockData "b1")
let Encoded b2 = encodeBlock (BlockData "b2")
let Encoded ix = encodeIndex (BlockIndex [10])
let inp = b1 <> b2 <> ix
bs <- runParse inp parseBlocks
bs `shouldBe` [Encoded b1, Encoded b2]
describe "basic data" $ do
it "should parse empty" $ do
af <- split ""
af `shouldBe` AsdfFile "" [] ""
it "just a tree" $ do
af <- split "tree"
af.tree `shouldBe` "tree"
af.blocks `shouldBe` []
af.index `shouldBe` ""
describe "optionals" $ do
it "tree + data!" $ do
let treeData = "#hello"
let tree = Encoded treeData
let blocks = [encodeBlock (BlockData "data")]
let ix = encodeIndex (blockIndex tree blocks)
let inp = concatAsdfFile $ AsdfFile{tree = Encoded treeData, blocks, index = ix}
af <- split inp
af.tree `shouldBe` "#hello"
af.blocks `shouldBe` blocks
af.index `shouldBe` ix
it "all parts" $ do
let treeData = "#hello\n"
let blocks = [encodeBlock (BlockData "data")]
let index = encodeIndex (BlockIndex [BS.length treeData])
let out = concatAsdfFile $ AsdfFile{tree = Encoded treeData, blocks, index}
af <- split out
af.tree `shouldBe` Encoded "#hello\n"
af.blocks `shouldBe` blocks
af.index `shouldBe` index
it "optional blocks" $ do
af <- split "asdf"
af `shouldBe` AsdfFile "asdf" [] ""
it "optional tree" $ do
let Encoded out = encodeBlock $ BlockData "hello"
af <- split out
af.tree `shouldBe` Encoded ""
af.blocks `shouldBe` [Encoded out]
af.index `shouldBe` ""
it "tree and index" $ do
let out = "tree\n" <> blockIndexHeader <> "index"
af <- split out
af.tree `shouldBe` "tree\n"
af.blocks `shouldBe` []
af.index `shouldBe` Encoded (blockIndexHeader <> "index")
describe "real asdf file" $ do
it "tree smaller than document " $ do
ExampleFileFix inp dp <- getFixture
BS.length dp.tree.bytes `shouldNotBe` BS.length inp
it "tree exists" $ do
ExampleFileFix _ dp <- getFixture
BS.length dp.tree.bytes `shouldSatisfy` P.gt 0
it "blocks exist" $ do
ExampleFileFix _ dp <- getFixture
length dp.blocks `shouldSatisfy` P.gt 0
it "index exists" $ do
ExampleFileFix _ dp <- getFixture
BS.length dp.index.bytes `shouldSatisfy` P.gt 0
where
split :: BS.ByteString -> IO AsdfFile
split inp = runAsdfM $ splitAsdfFile inp
runParse :: ByteString -> Eff [NonDet, State ByteString, IOE] a -> IO a
runParse inp eff = do
ret <- runEff . runState inp . runNonDet OnEmptyKeep $ eff
case ret of
(Left _, rest) -> fail $ "Failed parse at: " ++ show (BS.take 100 rest)
(Right a, _) -> pure a
data ExampleFileFix = ExampleFileFix {input :: BS.ByteString, file :: AsdfFile}
instance Fixture ExampleFileFix where
fixtureAction = do
inp <- BS.readFile "samples/example.asdf"
f <- runAsdfM $ splitAsdfFile inp
pure $ noCleanup $ ExampleFileFix inp f