{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TupleSections #-} {-# OPTIONS_GHC -fno-warn-incomplete-patterns #-} module Data.Conduit.AttoparsecSpec (spec) where import Control.Exception (fromException) import Test.Hspec import Control.Applicative ((<*), (<|>)) import Control.Monad import Control.Monad.Trans.Resource (runExceptionT) import qualified Data.Attoparsec.ByteString.Char8 import qualified Data.Attoparsec.Text import Data.Conduit import Data.Conduit.Attoparsec import qualified Data.Conduit.List as CL spec :: Spec spec = describe "Data.Conduit.AttoparsecSpec" $ do describe "error position" $ do it "works for text" $ do let input = ["aaa\na", "aaa\n\n", "aaa", "aab\n\naaaa"] badLine = 4 badCol = 6 badOff = 15 parser = Data.Attoparsec.Text.endOfInput <|> (Data.Attoparsec.Text.notChar 'b' >> parser) sink = sinkParser parser sink' = sinkParserEither parser ea <- runExceptionT $ CL.sourceList input $$ sink case ea of Left e -> case fromException e of Just pe -> do errorPosition pe `shouldBe` Position badLine badCol badOff ea' <- CL.sourceList input $$ sink' case ea' of Left pe -> errorPosition pe `shouldBe` Position badLine badCol badOff it "works for bytestring" $ do let input = ["aaa\na", "aaa\n\n", "aaa", "aab\n\naaaa"] badLine = 4 badCol = 6 badOff = 15 parser = Data.Attoparsec.ByteString.Char8.endOfInput <|> (Data.Attoparsec.ByteString.Char8.notChar 'b' >> parser) sink = sinkParser parser sink' = sinkParserEither parser ea <- runExceptionT $ CL.sourceList input $$ sink case ea of Left e -> case fromException e of Just pe -> do errorPosition pe `shouldBe` Position badLine badCol badOff ea' <- CL.sourceList input $$ sink' case ea' of Left pe -> errorPosition pe `shouldBe` Position badLine badCol badOff it "works in last chunk" $ do let input = ["aaa\na", "aaa\n\n", "aaa", "aab\n\naaaa"] badLine = 6 badCol = 5 badOff = 22 parser = Data.Attoparsec.Text.char 'c' <|> (Data.Attoparsec.Text.anyChar >> parser) sink = sinkParser parser sink' = sinkParserEither parser ea <- runExceptionT $ CL.sourceList input $$ sink case ea of Left e -> case fromException e of Just pe -> do errorPosition pe `shouldBe` Position badLine badCol badOff ea' <- CL.sourceList input $$ sink' case ea' of Left pe -> errorPosition pe `shouldBe` Position badLine badCol badOff it "works in last chunk" $ do let input = ["aaa\na", "aaa\n\n", "aaa", "aa\n\naaaab"] badLine = 6 badCol = 6 badOff = 22 parser = Data.Attoparsec.Text.string "bc" <|> (Data.Attoparsec.Text.anyChar >> parser) sink = sinkParser parser sink' = sinkParserEither parser ea <- runExceptionT $ CL.sourceList input $$ sink case ea of Left e -> case fromException e of Just pe -> do errorPosition pe `shouldBe` Position badLine badCol badOff ea' <- CL.sourceList input $$ sink' case ea' of Left pe -> errorPosition pe `shouldBe` Position badLine badCol badOff it "works after new line in text" $ do let input = ["aaa\n", "aaa\n\n", "aaa", "aa\nb\naaaa"] badLine = 5 badCol = 1 badOff = 15 parser = Data.Attoparsec.Text.endOfInput <|> (Data.Attoparsec.Text.notChar 'b' >> parser) sink = sinkParser parser sink' = sinkParserEither parser ea <- runExceptionT $ CL.sourceList input $$ sink case ea of Left e -> case fromException e of Just pe -> do errorPosition pe `shouldBe` Position badLine badCol badOff ea' <- CL.sourceList input $$ sink' case ea' of Left pe -> errorPosition pe `shouldBe` Position badLine badCol badOff it "works after new line in bytestring" $ do let input = ["aaa\n", "aaa\n\n", "aaa", "aa\nb\naaaa"] badLine = 5 badCol = 1 badOff = 15 parser = Data.Attoparsec.ByteString.Char8.endOfInput <|> (Data.Attoparsec.ByteString.Char8.notChar 'b' >> parser) sink = sinkParser parser sink' = sinkParserEither parser ea <- runExceptionT $ CL.sourceList input $$ sink case ea of Left e -> case fromException e of Just pe -> do errorPosition pe `shouldBe` Position badLine badCol badOff ea' <- CL.sourceList input $$ sink' case ea' of Left pe -> errorPosition pe `shouldBe` Position badLine badCol badOff it "works for first line" $ do let input = ["aab\na", "aaa\n\n", "aaa", "aab\n\naaaa"] badLine = 1 badCol = 3 badOff = 2 parser = Data.Attoparsec.Text.endOfInput <|> (Data.Attoparsec.Text.notChar 'b' >> parser) sink = sinkParser parser sink' = sinkParserEither parser ea <- runExceptionT $ CL.sourceList input $$ sink case ea of Left e -> case fromException e of Just pe -> do errorPosition pe `shouldBe` Position badLine badCol badOff ea' <- CL.sourceList input $$ sink' case ea' of Left pe -> errorPosition pe `shouldBe` Position badLine badCol badOff describe "conduitParser" $ do it "parses a repeated stream" $ do let input = ["aaa\n", "aaa\naaa\n", "aaa\n"] parser = Data.Attoparsec.Text.string "aaa" <* Data.Attoparsec.Text.endOfLine sink = conduitParserEither parser =$= CL.consume (Right ea) <- runExceptionT $ CL.sourceList input $$ sink let chk a = case a of Left{} -> False Right (_, xs) -> xs == "aaa" chkp l = PositionRange (Position l 1 ((l - 1) * 4)) (Position (l+1) 1 (l * 4)) forM_ ea $ \ a -> a `shouldSatisfy` chk :: Expectation forM_ (zip ea [1..]) $ \ (Right (pos, _), l) -> pos `shouldBe` chkp l length ea `shouldBe` 4 it "positions on first line" $ do results <- yield "hihihi\nhihi" $$ conduitParser (Data.Attoparsec.Text.string "\n" <|> Data.Attoparsec.Text.string "hi") =$ CL.consume let f (a, b, c, d, e, f, g) = (PositionRange (Position a b c) (Position d e f), g) results `shouldBe` map f [ (1, 1, 0, 1, 3, 2, "hi") , (1, 3, 2, 1, 5, 4, "hi") , (1, 5, 4, 1, 7, 6, "hi") , (1, 7, 6, 2, 1, 7, "\n") , (2, 1, 7, 2, 3, 9, "hi") , (2, 3, 9, 2, 5, 11, "hi") ]