range-0.3.2.0: Test/RangeParser.hs
module Test.RangeParser
( rangeParserTestCases
) where
import Test.Framework (Test, testGroup)
import Test.QuickCheck
import Test.Framework.Providers.QuickCheck2
import Data.Range
import Data.Range.Parser
-- ---------------------------------------------------------------------------
-- Helpers
-- ---------------------------------------------------------------------------
shouldParse :: String -> [Range Integer] -> Bool
shouldParse input expected = case parseRanges input of
Right result -> result == expected
Left _ -> False
shouldFail :: String -> Bool
shouldFail input = case (parseRanges input :: Either ParseError [Range Integer]) of
Left _ -> True
Right _ -> False
-- ---------------------------------------------------------------------------
-- Haddock example tests
-- ---------------------------------------------------------------------------
-- The example from the module documentation:
-- >>> parseRanges "-5,8-10,13-15,20-" :: Either ParseError [Range Integer]
-- Right [UpperBoundRange 5,SpanRange 8 10,SpanRange 13 15,LowerBoundRange 20]
prop_haddock_example :: Bool
prop_haddock_example = shouldParse "-5,8-10,13-15,20-"
[ UpperBoundRange (Bound 5 Inclusive)
, SpanRange (Bound 8 Inclusive) (Bound 10 Inclusive)
, SpanRange (Bound 13 Inclusive) (Bound 15 Inclusive)
, LowerBoundRange (Bound 20 Inclusive)
]
test_haddock :: Test
test_haddock = testGroup "haddock examples"
[ testProperty "documented example parses correctly" prop_haddock_example
]
-- ---------------------------------------------------------------------------
-- Singleton ranges
-- ---------------------------------------------------------------------------
prop_parse_singleton :: Positive Integer -> Bool
prop_parse_singleton (Positive n) = shouldParse (show n) [SingletonRange n]
prop_parse_singleton_zero :: Bool
prop_parse_singleton_zero = shouldParse "0" [SingletonRange 0]
test_singletons :: Test
test_singletons = testGroup "singleton ranges"
[ testProperty "positive integer parses as singleton" prop_parse_singleton
, testProperty "zero parses as singleton" prop_parse_singleton_zero
]
-- ---------------------------------------------------------------------------
-- Span ranges
-- ---------------------------------------------------------------------------
prop_parse_span :: (Positive Integer, Positive Integer) -> Bool
prop_parse_span (Positive a, Positive b) =
shouldParse (show a ++ "-" ++ show b)
[SpanRange (Bound a Inclusive) (Bound b Inclusive)]
test_spans :: Test
test_spans = testGroup "span ranges"
[ testProperty "a-b parses as span" prop_parse_span
]
-- ---------------------------------------------------------------------------
-- Bound ranges
-- ---------------------------------------------------------------------------
prop_parse_lower_bound :: Positive Integer -> Bool
prop_parse_lower_bound (Positive n) =
shouldParse (show n ++ "-") [LowerBoundRange (Bound n Inclusive)]
prop_parse_upper_bound :: Positive Integer -> Bool
prop_parse_upper_bound (Positive n) =
shouldParse ("-" ++ show n) [UpperBoundRange (Bound n Inclusive)]
test_bounds :: Test
test_bounds = testGroup "bound ranges"
[ testProperty "n- parses as lower bound" prop_parse_lower_bound
, testProperty "-n parses as upper bound" prop_parse_upper_bound
]
-- ---------------------------------------------------------------------------
-- Wildcard / infinite range
-- ---------------------------------------------------------------------------
prop_parse_wildcard :: Bool
prop_parse_wildcard = shouldParse "*" [InfiniteRange]
prop_parse_wildcard_in_union :: Bool
prop_parse_wildcard_in_union = shouldParse "*,5"
[InfiniteRange, SingletonRange 5]
test_wildcard :: Test
test_wildcard = testGroup "wildcard / infinite range"
[ testProperty "* parses as InfiniteRange" prop_parse_wildcard
, testProperty "* in union parses correctly" prop_parse_wildcard_in_union
]
-- ---------------------------------------------------------------------------
-- Union (comma-separated)
-- ---------------------------------------------------------------------------
prop_parse_union :: Bool
prop_parse_union = shouldParse "1,2,3"
[SingletonRange 1, SingletonRange 2, SingletonRange 3]
prop_parse_mixed_union :: Bool
prop_parse_mixed_union = shouldParse "5,10-20,30-"
[ SingletonRange 5
, SpanRange (Bound 10 Inclusive) (Bound 20 Inclusive)
, LowerBoundRange (Bound 30 Inclusive)
]
test_union :: Test
test_union = testGroup "union (comma-separated)"
[ testProperty "singletons separated by commas" prop_parse_union
, testProperty "mixed types separated by commas" prop_parse_mixed_union
]
-- ---------------------------------------------------------------------------
-- Edge cases and invalid inputs
-- ---------------------------------------------------------------------------
prop_empty_string_parses :: Bool
prop_empty_string_parses = case (parseRanges "" :: Either ParseError [Range Integer]) of
Right [] -> True
_ -> False
-- The parser uses sepBy which returns [] on no matches,
-- so non-range input like "abc" or "-" parses as Right [].
-- This is a known limitation of the current parser design.
prop_non_range_input_parses_empty :: Bool
prop_non_range_input_parses_empty =
case (parseRanges "abc" :: Either ParseError [Range Integer]) of
Right [] -> True
_ -> False
test_edge_cases :: Test
test_edge_cases = testGroup "edge cases"
[ testProperty "empty string produces empty list" prop_empty_string_parses
, testProperty "non-range input produces empty list" prop_non_range_input_parses_empty
]
-- ---------------------------------------------------------------------------
-- Custom parser args
-- ---------------------------------------------------------------------------
prop_custom_separators :: Bool
prop_custom_separators =
let args = defaultArgs { unionSeparator = ";", rangeSeparator = ".." }
result = customParseRanges args "1..5;10" :: Either ParseError [Range Integer]
in case result of
Right ranges -> ranges ==
[ SpanRange (Bound 1 Inclusive) (Bound 5 Inclusive)
, SingletonRange 10
]
Left _ -> False
test_custom :: Test
test_custom = testGroup "custom parser args"
[ testProperty "custom separators work" prop_custom_separators
]
-- ---------------------------------------------------------------------------
-- Export
-- ---------------------------------------------------------------------------
rangeParserTestCases :: [Test]
rangeParserTestCases =
[ test_haddock
, test_singletons
, test_spans
, test_bounds
, test_wildcard
, test_union
, test_edge_cases
, test_custom
]