2015-08-03 10:19:23 +03:00
|
|
|
|
-- -*- Mode: Haskell; -*-
|
|
|
|
|
--
|
|
|
|
|
-- QuickCheck tests for Megaparsec's parse errors.
|
|
|
|
|
--
|
2016-01-09 15:56:33 +03:00
|
|
|
|
-- Copyright © 2015–2016 Megaparsec contributors
|
2015-08-03 10:19:23 +03:00
|
|
|
|
--
|
|
|
|
|
-- Redistribution and use in source and binary forms, with or without
|
|
|
|
|
-- modification, are permitted provided that the following conditions are
|
|
|
|
|
-- met:
|
|
|
|
|
--
|
|
|
|
|
-- * Redistributions of source code must retain the above copyright notice,
|
|
|
|
|
-- this list of conditions and the following disclaimer.
|
|
|
|
|
--
|
|
|
|
|
-- * Redistributions in binary form must reproduce the above copyright
|
|
|
|
|
-- notice, this list of conditions and the following disclaimer in the
|
|
|
|
|
-- documentation and/or other materials provided with the distribution.
|
|
|
|
|
--
|
2015-10-30 16:41:21 +03:00
|
|
|
|
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS “AS IS” AND ANY
|
|
|
|
|
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
|
|
|
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
-- DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
|
|
|
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
|
-- DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
|
-- OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
|
-- HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
|
-- STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
|
|
|
-- ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
|
-- POSSIBILITY OF SUCH DAMAGE.
|
2015-08-03 10:19:23 +03:00
|
|
|
|
|
2015-08-03 20:44:11 +03:00
|
|
|
|
{-# OPTIONS -fno-warn-orphans #-}
|
|
|
|
|
|
2015-08-03 10:19:23 +03:00
|
|
|
|
module Error (tests) where
|
|
|
|
|
|
2015-08-11 05:54:33 +03:00
|
|
|
|
import Data.List (isPrefixOf, isInfixOf)
|
2015-10-21 16:26:57 +03:00
|
|
|
|
import Data.Monoid ((<>))
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
2015-08-03 10:19:23 +03:00
|
|
|
|
import Test.Framework
|
|
|
|
|
import Test.Framework.Providers.QuickCheck2 (testProperty)
|
|
|
|
|
import Test.QuickCheck
|
|
|
|
|
|
2015-08-03 20:44:11 +03:00
|
|
|
|
import Pos ()
|
2015-08-03 10:19:23 +03:00
|
|
|
|
import Text.Megaparsec.Error
|
2015-08-03 20:44:11 +03:00
|
|
|
|
import Text.Megaparsec.Pos
|
2015-08-03 10:19:23 +03:00
|
|
|
|
|
2015-09-30 20:30:50 +03:00
|
|
|
|
#if !MIN_VERSION_base(4,8,0)
|
|
|
|
|
import Control.Applicative ((<$>), (<*>))
|
2015-10-21 16:50:04 +03:00
|
|
|
|
import Data.Monoid (mempty)
|
2015-09-30 20:30:50 +03:00
|
|
|
|
#endif
|
|
|
|
|
|
2015-08-03 10:19:23 +03:00
|
|
|
|
tests :: Test
|
|
|
|
|
tests = testGroup "Parse errors"
|
2015-10-28 14:51:35 +03:00
|
|
|
|
[ testProperty "monoid left identity" prop_monoid_left_id
|
|
|
|
|
, testProperty "monoid right identity" prop_monoid_right_id
|
|
|
|
|
, testProperty "monoid associativity" prop_monoid_assoc
|
|
|
|
|
, testProperty "extraction of message string" prop_messageString
|
|
|
|
|
, testProperty "creation of new error messages" prop_newErrorMessage
|
2015-08-11 00:19:16 +03:00
|
|
|
|
, testProperty "messages are always well-formed" prop_wellFormedMessages
|
2015-10-28 14:51:35 +03:00
|
|
|
|
, testProperty "copying of error positions" prop_parseErrorCopy
|
|
|
|
|
, testProperty "setting of error position" prop_setErrorPos
|
|
|
|
|
, testProperty "addition of error message" prop_addErrorMessage
|
|
|
|
|
, testProperty "setting of error message" prop_setErrorMessage
|
|
|
|
|
, testProperty "position of merged error" prop_mergeErrorPos
|
|
|
|
|
, testProperty "messages of merged error" prop_mergeErrorMsgs
|
|
|
|
|
, testProperty "position of error is visible" prop_visiblePos
|
|
|
|
|
, testProperty "message components are visible" prop_visibleMsgs ]
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
instance Arbitrary Message where
|
2015-08-12 20:51:06 +03:00
|
|
|
|
arbitrary = ($) <$> elements constructors <*> arbitrary
|
|
|
|
|
where constructors = [Unexpected, Expected, Message]
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
instance Arbitrary ParseError where
|
2015-08-12 20:51:06 +03:00
|
|
|
|
arbitrary = do
|
2015-10-21 16:26:57 +03:00
|
|
|
|
ms <- listOf arbitrary
|
|
|
|
|
err <- oneof [ newErrorUnknown <$> arbitrary
|
|
|
|
|
, newErrorMessage <$> arbitrary <*> arbitrary ]
|
|
|
|
|
return $ addErrorMessages ms err
|
|
|
|
|
|
|
|
|
|
prop_monoid_left_id :: ParseError -> Bool
|
|
|
|
|
prop_monoid_left_id x = mempty <> x == x
|
|
|
|
|
|
|
|
|
|
prop_monoid_right_id :: ParseError -> Bool
|
|
|
|
|
prop_monoid_right_id x = x <> mempty == x
|
|
|
|
|
|
|
|
|
|
prop_monoid_assoc :: ParseError -> ParseError -> ParseError -> Bool
|
|
|
|
|
prop_monoid_assoc x y z = (x <> y) <> z == x <> (y <> z)
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_messageString :: Message -> Bool
|
2015-08-11 05:54:33 +03:00
|
|
|
|
prop_messageString m@(Unexpected s) = s == messageString m
|
|
|
|
|
prop_messageString m@(Expected s) = s == messageString m
|
|
|
|
|
prop_messageString m@(Message s) = s == messageString m
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_newErrorMessage :: Message -> SourcePos -> Bool
|
2015-08-11 05:54:33 +03:00
|
|
|
|
prop_newErrorMessage msg pos = added && errorPos new == pos
|
2015-08-12 20:51:06 +03:00
|
|
|
|
where new = newErrorMessage msg pos
|
2015-10-22 11:34:25 +03:00
|
|
|
|
added = errorMessages new == if badMessage msg then [] else [msg]
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
2015-08-11 00:19:16 +03:00
|
|
|
|
prop_wellFormedMessages :: ParseError -> Bool
|
2015-10-21 16:26:57 +03:00
|
|
|
|
prop_wellFormedMessages = wellFormed . errorMessages
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_parseErrorCopy :: ParseError -> Bool
|
|
|
|
|
prop_parseErrorCopy err =
|
2015-08-12 20:51:06 +03:00
|
|
|
|
foldr addErrorMessage (newErrorUnknown pos) msgs == err
|
|
|
|
|
where pos = errorPos err
|
|
|
|
|
msgs = errorMessages err
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_setErrorPos :: SourcePos -> ParseError -> Bool
|
|
|
|
|
prop_setErrorPos pos err =
|
2015-08-12 20:51:06 +03:00
|
|
|
|
errorPos new == pos && errorMessages new == errorMessages err
|
|
|
|
|
where new = setErrorPos pos err
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_addErrorMessage :: Message -> ParseError -> Bool
|
|
|
|
|
prop_addErrorMessage msg err =
|
2015-08-12 20:51:06 +03:00
|
|
|
|
wellFormed msgs && (badMessage msg || added)
|
|
|
|
|
where new = addErrorMessage msg err
|
|
|
|
|
msgs = errorMessages new
|
|
|
|
|
added = msg `elem` msgs && not (errorIsUnknown new)
|
2015-08-08 21:38:30 +03:00
|
|
|
|
|
|
|
|
|
prop_setErrorMessage :: Message -> ParseError -> Bool
|
|
|
|
|
prop_setErrorMessage msg err =
|
2015-08-12 20:51:06 +03:00
|
|
|
|
wellFormed msgs && (badMessage msg || (added && unique))
|
|
|
|
|
where new = setErrorMessage msg err
|
|
|
|
|
msgs = errorMessages new
|
|
|
|
|
added = msg `elem` msgs && not (errorIsUnknown new)
|
|
|
|
|
unique = length (filter (== fromEnum msg) (fromEnum <$> msgs)) == 1
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_mergeErrorPos :: ParseError -> ParseError -> Bool
|
2015-08-23 22:45:12 +03:00
|
|
|
|
prop_mergeErrorPos e1 e2 = errorPos (mergeError e1 e2) == max pos1 pos2
|
2015-08-12 20:51:06 +03:00
|
|
|
|
where pos1 = errorPos e1
|
|
|
|
|
pos2 = errorPos e2
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_mergeErrorMsgs :: ParseError -> ParseError -> Bool
|
2015-08-11 00:19:16 +03:00
|
|
|
|
prop_mergeErrorMsgs e1 e2' = errorPos e1 /= errorPos e2 || wellFormed msgsm
|
2015-08-12 20:51:06 +03:00
|
|
|
|
where e2 = setErrorPos (errorPos e1) e2'
|
|
|
|
|
msgsm = errorMessages $ mergeError e1 e2
|
2015-08-03 20:44:11 +03:00
|
|
|
|
|
|
|
|
|
prop_visiblePos :: ParseError -> Bool
|
|
|
|
|
prop_visiblePos err = show (errorPos err) `isPrefixOf` show err
|
|
|
|
|
|
|
|
|
|
prop_visibleMsgs :: ParseError -> Bool
|
2015-10-21 16:26:57 +03:00
|
|
|
|
prop_visibleMsgs err = if null msgs
|
|
|
|
|
then "unknown" `isInfixOf` shown
|
|
|
|
|
else all (`isInfixOf` shown) (msgs >>= f)
|
2015-08-12 20:51:06 +03:00
|
|
|
|
where shown = show err
|
2015-10-21 16:26:57 +03:00
|
|
|
|
msgs = errorMessages err
|
2015-08-12 20:51:06 +03:00
|
|
|
|
f (Unexpected s) = ["unexpected", s]
|
2015-10-28 14:51:35 +03:00
|
|
|
|
f (Expected s) = ["expecting", s]
|
2015-08-12 20:51:06 +03:00
|
|
|
|
f (Message s) = [s]
|
2015-08-08 21:38:30 +03:00
|
|
|
|
|
2015-10-21 16:26:57 +03:00
|
|
|
|
-- | @wellFormed xs@ checks that list @xs@ is sorted and contains no
|
2015-08-11 05:54:33 +03:00
|
|
|
|
-- duplicates and no empty messages.
|
2015-08-11 00:19:16 +03:00
|
|
|
|
|
2015-08-11 05:54:33 +03:00
|
|
|
|
wellFormed :: [Message] -> Bool
|
|
|
|
|
wellFormed xs = and (zipWith (<) xs (tail xs)) && not (any badMessage xs)
|