haskell-scfg

[MIRROR] Haskell library for scfg

git clone git://git.marius.pm/haskell-scfg.git

  1{-# LANGUAGE OverloadedStrings #-}
  2
  3module Main (main) where
  4
  5import Data.Either (isLeft)
  6import Data.Scfg
  7import System.IO (hClose, hPutStr)
  8import System.IO.Temp (withSystemTempFile)
  9import Test.Hspec
 10
 11main :: IO ()
 12main = hspec $ do
 13    describe "parse" $ do
 14        describe "directives" $ do
 15            it "parses a bare directive with no params" $
 16                parse "foo\n" `shouldBe` Right [Directive "foo" [] []]
 17
 18            it "parses a directive with params" $
 19                parse "foo bar baz\n" `shouldBe` Right [Directive "foo" ["bar", "baz"] []]
 20
 21            it "parses multiple directives" $
 22                parse "foo\nbar\n" `shouldBe` Right [Directive "foo" [] [], Directive "bar" [] []]
 23
 24            it "parses an empty config" $
 25                parse "" `shouldBe` Right []
 26
 27            it "accepts missing final newline" $
 28                parse "foo" `shouldBe` Right [Directive "foo" [] []]
 29
 30            it "ignores blank lines" $
 31                parse "foo\n\nbar\n" `shouldBe` Right [Directive "foo" [] [], Directive "bar" [] []]
 32
 33        describe "blocks" $ do
 34            it "parses a directive with a block" $
 35                parse "foo {\nbar\n}\n" `shouldBe` Right [Directive "foo" [] [Directive "bar" [] []]]
 36
 37            it "allows whitespace after opening brace" $
 38                parse "foo {  \nbar\n}\n" `shouldBe` Right [Directive "foo" [] [Directive "bar" [] []]]
 39
 40        describe "trailing whitespace" $ do
 41            it "allows trailing whitespace after params" $
 42                parse "foo bar \n" `shouldBe` Right [Directive "foo" ["bar"] []]
 43
 44            it "allows trailing whitespace with no params" $
 45                parse "foo \n" `shouldBe` Right [Directive "foo" [] []]
 46
 47        describe "comments" $ do
 48            it "ignores comments" $
 49                parse "# a comment\nfoo\n" `shouldBe` Right [Directive "foo" [] []]
 50
 51            it "rejects CTL characters in comments" $
 52                parse "# bad\1&comment\nfoo\n" `shouldSatisfy` isLeft
 53
 54    describe "ParseError" $ do
 55        it "includes line and column of the error" $ do
 56            let result = parse "foo\nbar 'unterminated\n"
 57            case result of
 58                Right _ -> expectationFailure "expected parse error"
 59                Left err -> do
 60                    errorLine err `shouldBe` 2
 61                    errorColumn err `shouldBe` 5
 62
 63        describe "atoms" $ do
 64            it "rejects single quote in atom" $
 65                parse "foo bar'baz\n" `shouldSatisfy` isLeft
 66
 67            it "rejects CTL characters" $
 68                parse "foo bar\1&baz\n" `shouldSatisfy` isLeft
 69
 70            it "allows C1 control characters" $
 71                parse "foo bar\x80baz\n" `shouldBe` Right [Directive "foo" ["bar\x80baz"] []]
 72
 73        describe "double-quoted words" $ do
 74            it "parses double-quoted params" $
 75                parse "foo \"hello world\"\n" `shouldBe` Right [Directive "foo" ["hello world"] []]
 76
 77            it "rejects CTL characters" $
 78                parse "foo \"bar\1&baz\"\n" `shouldSatisfy` isLeft
 79
 80            it "allows tab" $
 81                parse "foo \"bar\tbaz\"\n" `shouldBe` Right [Directive "foo" ["bar\tbaz"] []]
 82
 83            it "allows C1 control characters" $
 84                parse "foo \"bar\x80baz\"\n" `shouldBe` Right [Directive "foo" ["bar\x80baz"] []]
 85
 86        describe "single-quoted words" $ do
 87            it "parses single-quoted params" $
 88                parse "foo 'hello world'\n" `shouldBe` Right [Directive "foo" ["hello world"] []]
 89
 90            it "rejects newline" $
 91                parse "foo 'bar\nbaz'\n" `shouldSatisfy` isLeft
 92
 93            it "allows tab" $
 94                parse "foo 'bar\tbaz'\n" `shouldBe` Right [Directive "foo" ["bar\tbaz"] []]
 95
 96            it "allows C1 control characters" $
 97                parse "foo 'bar\x80baz'\n" `shouldBe` Right [Directive "foo" ["bar\x80baz"] []]
 98
 99        describe "escape sequences" $ do
100            it "parses escape sequences in atoms" $
101                parse "foo bar\\=baz\n" `shouldBe` Right [Directive "foo" ["bar=baz"] []]
102
103            it "parses escape sequences in double-quoted words" $
104                parse "foo \"bar\\\"baz\"\n" `shouldBe` Right [Directive "foo" ["bar\"baz"] []]
105
106            it "rejects CTL character" $
107                parse "foo bar\\\1&baz\n" `shouldSatisfy` isLeft
108
109            it "allows tab" $
110                parse "foo bar\\\tbaz\n" `shouldBe` Right [Directive "foo" ["bar\tbaz"] []]
111
112        describe "parseFile" $ do
113            it "parses a file" $
114                withSystemTempFile "scfg" $ \path h -> do
115                    hPutStr h "foo bar\n"
116                    hClose h
117                    result <- parseFile path
118                    result `shouldBe` Right [Directive "foo" ["bar"] []]
119
120            it "returns Left on parse error" $
121                withSystemTempFile "scfg" $ \path h -> do
122                    hPutStr h "foo 'unterminated\n"
123                    hClose h
124                    result <- parseFile path
125                    result `shouldSatisfy` isLeft
126
127    describe "format" $ do
128        it "formats a bare directive" $
129            format [Directive "foo" [] []] `shouldBe` "\"foo\"\n"
130
131        it "formats a directive with params" $
132            format [Directive "foo" ["bar", "baz"] []] `shouldBe` "\"foo\" \"bar\" \"baz\"\n"
133
134        it "formats a directive with a block" $
135            format [Directive "foo" [] [Directive "bar" [] []]]
136                `shouldBe` "\"foo\" {\n\t\"bar\"\n}\n"
137
138        it "formats nested blocks with correct indentation" $
139            format [Directive "foo" [] [Directive "bar" [] [Directive "baz" [] []]]]
140                `shouldBe` "\"foo\" {\n\t\"bar\" {\n\t\t\"baz\"\n\t}\n}\n"
141
142        it "escapes double quotes in names and params" $
143            format [Directive "fo\"o" ["ba\"r"] []] `shouldBe` "\"fo\\\"o\" \"ba\\\"r\"\n"
144
145    describe "integration" $ do
146        it "parses the spec example" $
147            parse
148                "train \"Shinkansen\" {\n\
149                \  model \"E5\" {\n\
150                \    max-speed 320km/h\n\
151                \    weight 453.5t\n\
152                \  }\n\
153                \}\n"
154                `shouldBe` Right
155                    [ Directive
156                        "train"
157                        ["Shinkansen"]
158                        [ Directive
159                            "model"
160                            ["E5"]
161                            [ Directive "max-speed" ["320km/h"] []
162                            , Directive "weight" ["453.5t"] []
163                            ]
164                        ]
165                    ]