@@ -2,7 +2,7 @@
2{-# LANGUAGE PackageImports #-}
2{-# LANGUAGE PackageImports #-}
3{-# LANGUAGE ScopedTypeVariables #-}
3{-# LANGUAGE ScopedTypeVariables #-}
5−import Text.Libyaml
5+import qualified Text.Libyaml as Y
6import qualified Data.ByteString.Char8 as B8
6import qualified Data.ByteString.Char8 as B8
8import Test.HUnit hiding (Test, path)
8import Test.HUnit hiding (Test, path)
@@ -16,6 +16,11 @@ import Control.Exception (try, SomeException)
16import Test.Hspec.Monadic
16import Test.Hspec.Monadic
17import Test.Hspec.HUnit ()
17import Test.Hspec.HUnit ()
20+import qualified Data.Yaml as D
22+import Data.Convertible.Text (cs)
19main :: IO ()
24main :: IO ()
20main = hspecX $ do
25main = hspecX $ do
21 describe "streaming" $ do
26 describe "streaming" $ do
@@ -29,17 +34,34 @@ main = hspecX $ do
29 it "encode/decode file" caseEncodeDecodeFile
34 it "encode/decode file" caseEncodeDecodeFile
30 it "interleaved encode/decode" caseInterleave
35 it "interleaved encode/decode" caseInterleave
31 it "decode invalid document (without segfault)" caseDecodeInvalidDocument
36 it "decode invalid document (without segfault)" caseDecodeInvalidDocument
37+ describe "Data.Yaml" $ do
38+ it "encode/decode" caseEncodeDecodeData
39+ it "encode/decode file" caseEncodeDecodeFileData
40+ it "encode/decode strings" caseEncodeDecodeStrings
41+ it "decode invalid file" caseDecodeInvalid
42+ it "encode/decode in order" caseInOrder
43+ describe "Data.Yaml aliases" $ do
44+ it "simple scalar alias" caseSimpleScalarAlias
45+ it "simple sequence alias" caseSimpleSequenceAlias
46+ it "simple mapping alias" caseSimpleMappingAlias
47+ it "mapping alias before anchor" caseMappingAliasBeforeAnchor
48+ it "mapping alias inside anchor" caseMappingAliasInsideAnchor
49+ it "scalar alias overriding" caseScalarAliasOverriding
50+ describe "Data.Yaml merge keys" $ do
51+ it "test uniqueness of keys" caseAllKeysShouldBeUnique
52+ it "test mapping merge" caseSimpleMappingMerge
53+ it "test sequence of mappings merging" caseMergeSequence
33−counter :: (Event -> Bool) -> C.Sink Event IO Int
55+counter :: (Y.Event -> Bool) -> C.Sink Y.Event IO Int
34counter pred' =
56counter pred' =
35 CL.fold (\cnt e -> (if pred' e then 1 else 0) + cnt) 0
57 CL.fold (\cnt e -> (if pred' e then 1 else 0) + cnt) 0
37caseHelper :: String
59caseHelper :: String
38− -> (Event -> Bool)
60+ -> (Y.Event -> Bool)
40 -> Assertion
62 -> Assertion
41caseHelper yamlString pred' expRes = do
63caseHelper yamlString pred' expRes = do
42− res <- C.runResourceT $ decode (B8.pack yamlString) C.$$ counter pred'
64+ res <- C.runResourceT $ Y.decode (B8.pack yamlString) C.$$ counter pred'
43 res @?= expRes
65 res @?= expRes
45caseCountScalarsWithAnchor :: Assertion
67caseCountScalarsWithAnchor :: Assertion
@@ -47,7 +69,7 @@ caseCountScalarsWithAnchor =
47 caseHelper yamlString isScalarA 1
69 caseHelper yamlString isScalarA 1
49 yamlString = "foo:\n - &anchor bin1\n - bin2\n - bin3"
71 yamlString = "foo:\n - &anchor bin1\n - bin2\n - bin3"
50− isScalarA (EventScalar _ _ _ (Just _)) = True
72+ isScalarA (Y.EventScalar _ _ _ (Just _)) = True
51 isScalarA _ = False
73 isScalarA _ = False
53caseCountSequencesWithAnchor :: Assertion
75caseCountSequencesWithAnchor :: Assertion
@@ -55,7 +77,7 @@ caseCountSequencesWithAnchor =
55 caseHelper yamlString isSequenceStartA 1
77 caseHelper yamlString isSequenceStartA 1
57 yamlString = "foo: &anchor\n - bin1\n - bin2\n - bin3"
79 yamlString = "foo: &anchor\n - bin1\n - bin2\n - bin3"
58− isSequenceStartA (EventSequenceStart (Just _)) = True
80+ isSequenceStartA (Y.EventSequenceStart (Just _)) = True
59 isSequenceStartA _ = False
81 isSequenceStartA _ = False
61caseCountMappingsWithAnchor :: Assertion
83caseCountMappingsWithAnchor :: Assertion
@@ -63,7 +85,7 @@ caseCountMappingsWithAnchor =
63 caseHelper yamlString isMappingA 1
85 caseHelper yamlString isMappingA 1
65 yamlString = "foo: &anchor\n key1: bin1\n key2: bin2\n key3: bin3"
87 yamlString = "foo: &anchor\n key1: bin1\n key2: bin2\n key3: bin3"
66− isMappingA (EventMappingStart (Just _)) = True
88+ isMappingA (Y.EventMappingStart (Just _)) = True
67 isMappingA _ = False
89 isMappingA _ = False
69caseCountAliases :: Assertion
91caseCountAliases :: Assertion
@@ -71,47 +93,47 @@ caseCountAliases =
71 caseHelper yamlString isAlias 1
93 caseHelper yamlString isAlias 1
73 yamlString = "foo: &anchor\n key1: bin1\n key2: bin2\n key3: bin3\nboo: *anchor"
95 yamlString = "foo: &anchor\n key1: bin1\n key2: bin2\n key3: bin3\nboo: *anchor"
74− isAlias EventAlias{} = True
96+ isAlias Y.EventAlias{} = True
75 isAlias _ = False
97 isAlias _ = False
77caseCountScalars :: Assertion
99caseCountScalars :: Assertion
78caseCountScalars = do
100caseCountScalars = do
79− res <- C.runResourceT $ decode yamlBS C.$$ CL.fold adder accum
101+ res <- C.runResourceT $ Y.decode yamlBS C.$$ CL.fold adder accum
80 res @?= (7, 1, 2)
102 res @?= (7, 1, 2)
82 yamlString = "foo:\n baz: [bin1, bin2, bin3]\nbaz: bazval"
104 yamlString = "foo:\n baz: [bin1, bin2, bin3]\nbaz: bazval"
83 yamlBS = B8.pack yamlString
105 yamlBS = B8.pack yamlString
84− adder (s, l, m) (EventScalar{}) = (s + 1, l, m)
106+ adder (s, l, m) (Y.EventScalar{}) = (s + 1, l, m)
85− adder (s, l, m) (EventSequenceStart{}) = (s, l + 1, m)
107+ adder (s, l, m) (Y.EventSequenceStart{}) = (s, l + 1, m)
86− adder (s, l, m) (EventMappingStart{}) = (s, l, m + 1)
108+ adder (s, l, m) (Y.EventMappingStart{}) = (s, l, m + 1)
87 adder a _ = a
109 adder a _ = a
88 accum = (0, 0, 0) :: (Int, Int, Int)
110 accum = (0, 0, 0) :: (Int, Int, Int)
90caseLargestString :: Assertion
112caseLargestString :: Assertion
91caseLargestString = do
113caseLargestString = do
92− res <- C.runResourceT $ decodeFile filePath C.$$ CL.fold adder accum
114+ res <- C.runResourceT $ Y.decodeFile filePath C.$$ CL.fold adder accum
93 res @?= (length expected, expected)
115 res @?= (length expected, expected)
95 expected = "this one is just a little bit bigger than the others"
117 expected = "this one is just a little bit bigger than the others"
96 filePath = "test/largest-string.yaml"
118 filePath = "test/largest-string.yaml"
97− adder (i, s) (EventScalar bs _ _ _) =
119+ adder (i, s) (Y.EventScalar bs _ _ _) =
98 let s' = B8.unpack bs
120 let s' = B8.unpack bs
99 i' = length s'
121 i' = length s'
100 in if i' > i then (i', s') else (i, s)
122 in if i' > i then (i', s') else (i, s)
101 adder acc _ = acc
123 adder acc _ = acc
102 accum = (0, "no strings found")
124 accum = (0, "no strings found")
104−newtype MyEvent = MyEvent Event deriving Show
126+newtype MyEvent = MyEvent Y.Event deriving Show
105instance Eq MyEvent where
127instance Eq MyEvent where
106− (MyEvent (EventScalar s t _ _)) == (MyEvent (EventScalar s' t' _ _)) =
128+ (MyEvent (Y.EventScalar s t _ _)) == (MyEvent (Y.EventScalar s' t' _ _)) =
107 s == s' && t == t'
129 s == s' && t == t'
108 MyEvent e1 == MyEvent e2 = e1 == e2
130 MyEvent e1 == MyEvent e2 = e1 == e2
110caseEncodeDecode :: Assertion
132caseEncodeDecode :: Assertion
111caseEncodeDecode = do
133caseEncodeDecode = do
112− eList <- C.runResourceT $ decode yamlBS C.$$ CL.consume
134+ eList <- C.runResourceT $ Y.decode yamlBS C.$$ CL.consume
113− bs <- C.runResourceT $ CL.sourceList eList C.$$ encode
135+ bs <- C.runResourceT $ CL.sourceList eList C.$$ Y.encode
114− eList2 <- C.runResourceT $ decode bs C.$$ CL.consume
136+ eList2 <- C.runResourceT $ Y.decode bs C.$$ CL.consume
115 map MyEvent eList @=? map MyEvent eList2
137 map MyEvent eList @=? map MyEvent eList2
117 yamlString = "foo: bar\nbaz:\n - bin1\n - bin2\n"
139 yamlString = "foo: bar\nbaz:\n - bin1\n - bin2\n"
@@ -125,9 +147,9 @@ removeFile' fp = do
125caseEncodeDecodeFile :: Assertion
147caseEncodeDecodeFile :: Assertion
126caseEncodeDecodeFile = do
148caseEncodeDecodeFile = do
127 removeFile' tmpPath
149 removeFile' tmpPath
128− eList <- C.runResourceT $ decodeFile filePath C.$$ CL.consume
150+ eList <- C.runResourceT $ Y.decodeFile filePath C.$$ CL.consume
129− C.runResourceT $ CL.sourceList eList C.$$ encodeFile tmpPath
151+ C.runResourceT $ CL.sourceList eList C.$$ Y.encodeFile tmpPath
130− eList2 <- C.runResourceT $ decodeFile filePath C.$$ CL.consume
152+ eList2 <- C.runResourceT $ Y.decodeFile filePath C.$$ CL.consume
131 map MyEvent eList @=? map MyEvent eList2
153 map MyEvent eList @=? map MyEvent eList2
133 filePath = "test/largest-string.yaml"
155 filePath = "test/largest-string.yaml"
@@ -137,8 +159,8 @@ caseInterleave :: Assertion
137caseInterleave = do
159caseInterleave = do
138 removeFile' tmpPath
160 removeFile' tmpPath
139 removeFile' tmpPath2
161 removeFile' tmpPath2
140− () <- C.runResourceT $ decodeFile filePath C.$$ encodeFile tmpPath
162+ () <- C.runResourceT $ Y.decodeFile filePath C.$$ Y.encodeFile tmpPath
141− () <- C.runResourceT $ decodeFile tmpPath C.$$ encodeFile tmpPath2
163+ () <- C.runResourceT $ Y.decodeFile tmpPath C.$$ Y.encodeFile tmpPath2
142 f1 <- readFile tmpPath
164 f1 <- readFile tmpPath
143 f2 <- readFile tmpPath2
165 f2 <- readFile tmpPath2
144 f1 @=? f2
166 f1 @=? f2
@@ -149,7 +171,7 @@ caseInterleave = do
150caseDecodeInvalidDocument :: Assertion
172caseDecodeInvalidDocument :: Assertion
151caseDecodeInvalidDocument = do
173caseDecodeInvalidDocument = do
152− x <- try $ C.runResourceT $ decode yamlBS C.$$ CL.sinkNull
174+ x <- try $ C.runResourceT $ Y.decode yamlBS C.$$ CL.sinkNull
153 case x of
175 case x of
154 Left (_ :: SomeException) -> return ()
176 Left (_ :: SomeException) -> return ()
155 Right y -> do
177 Right y -> do
@@ -158,3 +180,130 @@ caseDecodeInvalidDocument = do
159 yamlString = " - foo\n - baz\nbuz"
181 yamlString = " - foo\n - baz\nbuz"
160 yamlBS = B8.pack yamlString
182 yamlBS = B8.pack yamlString
184+mkFoldedScalar :: String -> D.YamlScalar
185+mkFoldedScalar s = D.YamlScalar (cs s) Y.StrTag Y.Folded
187+mkScalar :: String -> D.YamlScalar
188+mkScalar s = D.YamlScalar (cs s) Y.NoTag Y.Plain
190+mkStrScalar :: String -> D.YamlScalar
191+mkStrScalar s = D.YamlScalar (cs s) Y.StrTag Y.Plain
193+mappingKey :: D.YamlObject -> String -> D.YamlObject
194+mappingKey (Mapping m) k = (fromJust . lookup (mkScalar k) $ m)
195+mappingKey _ _ = error "expected Mapping"
197+decodeYaml :: String -> Maybe D.YamlObject
198+decodeYaml s = D.decode $ B8.pack s
200+sample :: D.YamlObject
202+ [ Scalar $ mkFoldedScalar "foo"
204+ [ (mkFoldedScalar "bar1", Scalar $ mkFoldedScalar "bar2")
208+sampleStr :: Object String String
209+sampleStr = mapKeysValues D.fromYamlScalar D.fromYamlScalar sample
211+caseEncodeDecodeData :: Assertion
212+caseEncodeDecodeData = do
213+ out <- D.decode $ D.encode sample
216+caseEncodeDecodeFileData :: Assertion
217+caseEncodeDecodeFileData = do
219+ D.encodeFile fp sample
220+ out <- join $ D.decodeFile fp
223+caseEncodeDecodeStrings :: Assertion
224+caseEncodeDecodeStrings = do
225+ out <- D.decode $ D.encode $ D.toYamlObject sampleStr
226+ D.fromYamlObject out @?= sampleStr
228+caseDecodeInvalid :: Assertion
229+caseDecodeInvalid = do
230+ let invalid = B8.pack "\tthis is 'not' valid :-)"
231+ Nothing @=? (D.decode invalid :: Maybe D.YamlObject)
233+caseSimpleScalarAlias :: Assertion
234+caseSimpleScalarAlias = do
235+ let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch"
236+ isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
237+ let res = fromJust maybeRes
238+ res @?= Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz"), Scalar (mkScalar "foo")]
240+caseSimpleSequenceAlias :: Assertion
241+caseSimpleSequenceAlias = do
242+ let maybeRes = decodeYaml "seq: &anch\n - foo\n - baz\nseq2: *anch"
243+ isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
244+ let res = fromJust maybeRes
245+ res @?= Mapping [(mkScalar "seq", Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz")]), (mkScalar "seq2", Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz")])]
247+caseSimpleMappingAlias :: Assertion
248+caseSimpleMappingAlias = do
249+ let maybeRes = decodeYaml "map: &anch\n key1: foo\n key2: baz\nmap2: *anch"
250+ isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
251+ let res = fromJust maybeRes
252+ res @?= Mapping [(mkScalar "map", Mapping [(mkScalar "key1", Scalar (mkScalar "foo")), (mkScalar "key2", Scalar (mkScalar "baz"))]), (mkScalar "map2", Mapping [(mkScalar "key1", Scalar (mkScalar "foo")), (mkScalar "key2", Scalar (mkScalar "baz"))])]
254+caseMappingAliasBeforeAnchor :: Assertion
255+caseMappingAliasBeforeAnchor = do
256+ let res = decodeYaml "map: *anch\nmap2: &anch\n key1: foo\n key2: baz"
257+ isNothing res @? "decode should return Nothing due to unknown alias"
259+caseMappingAliasInsideAnchor :: Assertion
260+caseMappingAliasInsideAnchor = do
261+ let res = decodeYaml "map: &anch\n key1: foo\n key2: *anch"
262+ isNothing res @? "decode should return Nothing due to unknown alias"
264+caseScalarAliasOverriding :: Assertion
265+caseScalarAliasOverriding = do
266+ let maybeRes = decodeYaml "- &anch foo\n- baz\n- *anch\n- &anch boo\n- buz\n- *anch"
267+ isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
268+ let res = fromJust maybeRes
269+ res @?= Sequence [Scalar (mkScalar "foo"), Scalar (mkScalar "baz"), Scalar (mkScalar "foo"), Scalar (mkScalar "boo"), Scalar (mkScalar "buz"), Scalar (mkScalar "boo")]
271+caseAllKeysShouldBeUnique :: Assertion
272+caseAllKeysShouldBeUnique = do
273+ let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\nfoo1: buz"
274+ isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
275+ let res = fromJust maybeRes
276+ mappingKey res "foo1" @?= Scalar (mkScalar "buz")
278+caseSimpleMappingMerge :: Assertion
279+caseSimpleMappingMerge = do
280+ let maybeRes = decodeYaml "foo1: foo\nfoo2: baz\n<<:\n foo1: buz\n foo3: fuz"
281+ isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
282+ let res = fromJust maybeRes
283+ mappingKey res "foo1" @?= Scalar (mkScalar "foo")
284+ mappingKey res "foo3" @?= Scalar (mkScalar "fuz")
286+caseMergeSequence :: Assertion
287+caseMergeSequence = do
288+ let maybeRes = decodeYaml "m1: &m1\n k1: !!str 1\n k2: !!str 2\nm2: &m2\n k1: !!str 3\n k3: !!str 4\nfoo1: foo\n<<: [ *m1, *m2 ]"
289+ isJust maybeRes @? "decoder should return Just YamlObject but returned Nothing"
290+ let res = fromJust maybeRes
291+ mappingKey res "foo1" @?= Scalar (mkScalar "foo")
292+ mappingKey res "k1" @?= Scalar (mkStrScalar "1")
293+ mappingKey res "k2" @?= Scalar (mkStrScalar "2")
294+ mappingKey res "k3" @?= Scalar (mkStrScalar "4")
296+inOrderData :: String
297+inOrderData = "'Fatal': 'Unknown variable \"bar\"'\n'Date': '2001-11-23 15:03:17 -5'\n'User': 'ed'\n'Stack':\n- 'line': '23'\n 'file': 'TopClass.py'\n 'code': 'x = MoreObject(\"345\\n\")\n\n'\n- 'line': '58'\n 'file': 'MoreClass.py'\n 'code': 'foo = bar'\n"
299+inOrderData2 :: String
301+ "'a': '1'\n'b': '2'\n'd': '4'\n'c': '3'\n"
302+ ++ "'g': '1'\n'n': '2'\n'q': '4'\n'f': '3'\n"
303+ ++ "'z': '1'\n'y': '2'\n'x': '4'\n'w': '3'\n"
305+caseInOrder :: Assertion
307+ Just (Mapping ((x, _):_)) <- return $ decodeYaml inOrderData
308+ x @?= mkScalar "Fatal"
309+ fmap (B8.unpack . D.encode) (decodeYaml inOrderData2) @?= Just inOrderData2