@@ -5,27 +5,44 @@ module Text.Megaparsec.TimeSpec
5
5
) where
6
6
7
7
import Control.Monad (forM_ )
8
+ import Data.Bifunctor (Bifunctor (first ))
8
9
import Data.Either (isLeft )
9
10
import Data.List.Extra (lower )
10
11
import Data.Time (DayOfWeek (.. ), TimeOfDay (.. ),
11
- defaultTimeLocale , formatTime )
12
+ defaultTimeLocale , formatTime ,
13
+ secondsToNominalDiffTime )
14
+ import Data.Void (Void )
12
15
import Test.Hspec (Spec , context , describe , it , shouldBe ,
13
16
shouldSatisfy )
14
- import Test.QuickCheck (Arbitrary (.. ), elements , forAll ,
17
+ import Test.QuickCheck (Arbitrary (.. ), Gen , elements , forAll ,
15
18
property , suchThat )
16
- import Text.Megaparsec (runParser )
17
- import Text.Megaparsec.Time (dayParser , timeParser )
19
+ import Text.Megaparsec (Parsec , errorBundlePretty , runParser )
20
+ import Text.Megaparsec.Time (dayParser , durationParser , hoursParser ,
21
+ minutesParser , secondsParser , timeParser )
18
22
import Text.Printf (printf )
19
23
20
24
instance Arbitrary TimeOfDay where
21
- arbitrary = TimeOfDay
22
- <$> ((`mod` 24 ) . abs <$> arbitrary)
25
+ arbitrary =
26
+ (TimeOfDay . (`mod` 24 ) . abs
27
+ <$> arbitrary)
23
28
<*> ((`mod` 60 ) . abs <$> arbitrary)
24
29
<*> pure 0
25
30
26
31
instance Arbitrary DayOfWeek where
27
32
arbitrary = elements [Monday .. Sunday ]
28
33
34
+ positive
35
+ :: Num a
36
+ => Arbitrary a
37
+ => Gen a
38
+ positive = abs <$> arbitrary
39
+
40
+ parseOrPrettyError
41
+ :: Parsec Void String a
42
+ -> String
43
+ -> Either String a
44
+ parseOrPrettyError p = first errorBundlePretty . runParser p " test"
45
+
29
46
spec :: Spec
30
47
spec = do
31
48
describe " day" $ do
@@ -37,31 +54,81 @@ spec = do
37
54
] $ \ (title, format) ->
38
55
context title $ do
39
56
it " nominal" . forAll (arbitrary `suchThat` weekday) $ \ d ->
40
- runParser dayParser " day " (formatTime defaultTimeLocale format d) `shouldBe`
57
+ parseOrPrettyError dayParser (formatTime defaultTimeLocale format d) `shouldBe`
41
58
Right (Right d)
42
59
43
60
it " lowercase" . forAll (arbitrary `suchThat` weekday) $ \ d ->
44
- runParser dayParser " day " (lower (formatTime defaultTimeLocale format d)) `shouldBe`
61
+ parseOrPrettyError dayParser (lower (formatTime defaultTimeLocale format d)) `shouldBe`
45
62
Right (Right d)
46
63
47
64
it " weekend" . forAll (arbitrary `suchThat` (not . weekday)) $ \ d ->
48
- runParser dayParser " day " (formatTime defaultTimeLocale format d) `shouldSatisfy`
65
+ parseOrPrettyError dayParser (formatTime defaultTimeLocale format d) `shouldSatisfy`
49
66
isLeft
50
67
51
68
context " tomorrow" $ do
52
69
it " capitalized" $
53
- runParser dayParser " day " " Tomorrow " `shouldBe` Right (Left 1 )
70
+ parseOrPrettyError dayParser " Tomorrow " `shouldBe` Right (Left 1 )
54
71
55
72
it " lowercase" $
56
- runParser dayParser " day " " tomorrow " `shouldBe` Right (Left 1 )
73
+ parseOrPrettyError dayParser " tomorrow " `shouldBe` Right (Left 1 )
57
74
58
75
it " future day" . forAll (abs <$> arbitrary) $ \ x ->
59
- runParser dayParser " day " (printf " +%d" x) `shouldBe` Right (Left x)
76
+ parseOrPrettyError dayParser (printf " +%d" x) `shouldBe` Right (Left x)
60
77
61
78
it " invalid" . forAll (negate . (+ 1 ) . abs <$> arbitrary) $ \ x ->
62
- runParser dayParser " day" (printf " +%d" (x :: Int )) `shouldSatisfy` isLeft
79
+ parseOrPrettyError dayParser (printf " +%d" (x :: Int )) `shouldSatisfy` isLeft
80
+
81
+ describe " duration" $ do
82
+ it " hours" . forAll positive $ \ h ->
83
+ parseOrPrettyError hoursParser (printf " %dh" h) `shouldBe`
84
+ Right (secondsToNominalDiffTime (fromInteger h * 3600 ))
85
+
86
+ it " minutes" . forAll positive $ \ m ->
87
+ parseOrPrettyError minutesParser (printf " %dm" m) `shouldBe`
88
+ Right (secondsToNominalDiffTime (fromInteger m * 60 ))
89
+
90
+ context " seconds" $ do
91
+ it " raw" . forAll positive $ \ s ->
92
+ parseOrPrettyError secondsParser (printf " %d" s) `shouldBe`
93
+ Right (secondsToNominalDiffTime (fromInteger s))
94
+
95
+ it " with suffix" . forAll positive $ \ s ->
96
+ parseOrPrettyError secondsParser (printf " %ds" s) `shouldBe`
97
+ Right (secondsToNominalDiffTime (fromInteger s))
98
+
99
+ context " duration" $ do
100
+ context " hours" $ do
101
+ it " hms" . forAll ((,,) <$> positive <*> positive <*> positive) $ \ (h, m, s) ->
102
+ parseOrPrettyError durationParser (printf " %dh %dm %ds" h m s) `shouldBe`
103
+ Right (secondsToNominalDiffTime (fromInteger (((h * 60 + m) * 60 ) + s)))
104
+
105
+ it " hm" . forAll ((,) <$> positive <*> positive) $ \ (h, m) ->
106
+ parseOrPrettyError durationParser (printf " %dh %dm" h m) `shouldBe`
107
+ Right (secondsToNominalDiffTime (fromInteger ((h * 60 + m) * 60 )))
108
+
109
+ it " hs" . forAll ((,) <$> positive <*> positive) $ \ (h, s) ->
110
+ parseOrPrettyError durationParser (printf " %dh %ds" h s) `shouldBe`
111
+ Right (secondsToNominalDiffTime (fromInteger (h * 3600 + s)))
112
+
113
+ context " minutes" $ do
114
+ it " ms" . forAll ((,) <$> positive <*> positive) $ \ (m, s) ->
115
+ parseOrPrettyError durationParser (printf " %dm%ds" m s) `shouldBe`
116
+ Right (secondsToNominalDiffTime (fromInteger (m * 60 + s)))
117
+
118
+ it " m" . forAll positive $ \ m ->
119
+ parseOrPrettyError durationParser (printf " %dm" m) `shouldBe`
120
+ Right (secondsToNominalDiffTime (fromInteger (m * 60 )))
121
+
122
+ context " seconds" $ do
123
+ it " s" . forAll positive $ \ s ->
124
+ parseOrPrettyError durationParser (printf " %ds" s) `shouldBe`
125
+ Right (secondsToNominalDiffTime (fromInteger s))
126
+
127
+ it " no suffix" . forAll positive $ \ s ->
128
+ parseOrPrettyError durationParser (show s) `shouldBe`
129
+ Right (secondsToNominalDiffTime (fromInteger s))
63
130
64
131
describe " time" $ do
65
132
it " valid" . property $ \ t ->
66
- runParser timeParser " time " (formatTime defaultTimeLocale " %R" t) `shouldBe`
133
+ parseOrPrettyError timeParser (formatTime defaultTimeLocale " %R" t) `shouldBe`
67
134
Right t
0 commit comments