hspec-megaparsec-2.2.0/0000755000000000000000000000000007346545000013113 5ustar0000000000000000hspec-megaparsec-2.2.0/CHANGELOG.md0000644000000000000000000000255407346545000014732 0ustar0000000000000000## Hspec Megaparsec 2.2.0 * Works with Megaparsec 9. ## Hspec Megaparsec 2.1.0 * Works with Megaparsec 8. * Dropped support for GHC 8.2. ## Hspec Megaparsec 2.0.1 * Adjusted `shouldParse` to use `shouldBe` from `hspec` under the hood to take advantage of its pretty colorful error reporting. * Dropped support for GHC 8.0 and older. ## Hspec Megaparsec 2.0.0 * To be used with Megaparsec 7. * Added functions: `shouldFailWithM` and `initialPosState`. * Dropped support for GHC 7.8. ## Hspec Megaparsec 1.1.0 * Add `HasCallStack` constraint to combinators to improve detection of locations where test failures happen. ## Hspec Megaparsec 1.0.0 * To be used with Megaparsec 6. ## Hspec Megaparsec 0.3.1 * Support for Megaparsec 5.2.0. * Drop support for GHC 7.6. ## Hspec Megaparsec 0.3.0 * Added helpers for parse error construction (useful with `shouldFailWith`): `err`, `posI`, `posN`, `utok`, `utoks`, `ulabel`, `ueof`, `etok`, `etoks`, `elabel`, `eeof`, `cstm`. Also added an auxiliary type `EC`. ## Hspec Megaparsec 0.2.1 * Refreshed obsolete documentation for `shouldFailWith` and how it reports not matching parse errors. ## Hspec Megaparsec 0.2.0 * This version of `hspec-megaparsec` should be used with Megaparsec 5. ## Hspec Megaparsec 0.1.1 * Make it pass tests with Megaparsec 4.4.0 and later. ## Hspec Megaparsec 0.1.0 * Initial release. hspec-megaparsec-2.2.0/LICENSE.md0000644000000000000000000000265607346545000014530 0ustar0000000000000000Copyright © 2016–present Mark Karpov All rights reserved. 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. * Neither the name Mark Karpov nor the names of contributors may be used to endorse or promote products derived from this software without specific prior written permission. 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. hspec-megaparsec-2.2.0/README.md0000644000000000000000000000254707346545000014402 0ustar0000000000000000# Hspec Megaparsec [![License BSD3](https://img.shields.io/badge/license-BSD3-brightgreen.svg)](http://opensource.org/licenses/BSD-3-Clause) [![Hackage](https://img.shields.io/hackage/v/hspec-megaparsec.svg?style=flat)](https://hackage.haskell.org/package/hspec-megaparsec) [![Stackage Nightly](http://stackage.org/package/hspec-megaparsec/badge/nightly)](http://stackage.org/nightly/package/hspec-megaparsec) [![Stackage LTS](http://stackage.org/package/hspec-megaparsec/badge/lts)](http://stackage.org/lts/package/hspec-megaparsec) ![CI](https://github.com/mrkkrp/hspec-megaparsec/workflows/CI/badge.svg?branch=master) The package is the recommended library for testing [`Megaparsec`](https://hackage.haskell.org/package/megaparsec) parsers with with [`Hspec`](https://hackage.haskell.org/package/hspec). As of Megaparsec 5.1.0, its test suite is re-written with Hspec and this package with a few ad-hoc helpers. Consult the Haddocks for usage, which should be trivial. Also see test suite of this package or [Megaparsec test suite](https://github.com/mrkkrp/megaparsec/tree/master/tests). ## Contribution Issues, bugs, and questions may be reported in [the GitHub issue tracker for this project](https://github.com/mrkkrp/hspec-megaparsec/issues). Pull requests are also welcome. ## License Copyright © 2016–present Mark Karpov Distributed under BSD 3 clause license. hspec-megaparsec-2.2.0/Setup.hs0000644000000000000000000000012707346545000014547 0ustar0000000000000000module Main (main) where import Distribution.Simple main :: IO () main = defaultMain hspec-megaparsec-2.2.0/Test/Hspec/0000755000000000000000000000000007346545000015074 5ustar0000000000000000hspec-megaparsec-2.2.0/Test/Hspec/Megaparsec.hs0000644000000000000000000002124207346545000017500 0ustar0000000000000000{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} -- | -- Module : Test.Hspec.Megaparsec -- Copyright : © 2016–present Mark Karpov -- License : BSD 3 clause -- -- Maintainer : Mark Karpov -- Stability : experimental -- Portability : portable -- -- Utility functions for testing Megaparsec parsers with Hspec. module Test.Hspec.Megaparsec ( -- * Basic expectations shouldParse, parseSatisfies, shouldSucceedOn, shouldFailOn, -- * Testing of error messages shouldFailWith, shouldFailWithM, -- * Incremental parsing failsLeaving, succeedsLeaving, initialState, initialPosState, -- * Re-exports module Text.Megaparsec.Error.Builder, ) where import Control.Monad (unless) import qualified Data.List.NonEmpty as NE import Test.Hspec.Expectations import Text.Megaparsec import Text.Megaparsec.Error.Builder ---------------------------------------------------------------------------- -- Basic expectations -- | Create an expectation by saying what the result should be. -- -- > parse letterChar "" "x" `shouldParse` 'x' shouldParse :: ( HasCallStack, ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s, Show a, Eq a ) => -- | Result of parsing as returned by function like 'parse' Either (ParseErrorBundle s e) a -> -- | Desired result a -> Expectation r `shouldParse` v = case r of Left e -> expectationFailure $ "expected: " ++ show v ++ "\nbut parsing failed with error:\n" ++ showBundle e Right x -> x `shouldBe` v -- | Create an expectation by saying that the parser should successfully -- parse a value and that the value should satisfy some predicate. -- -- > parse (many punctuationChar) "" "?!!" `parseSatisfies` ((== 3) . length) parseSatisfies :: ( HasCallStack, ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s, Show a, Eq a ) => -- | Result of parsing as returned by function like 'parse' Either (ParseErrorBundle s e) a -> -- | Predicate (a -> Bool) -> Expectation r `parseSatisfies` p = case r of Left e -> expectationFailure $ "expected a parsed value to check against the predicate" ++ "\nbut parsing failed with error:\n" ++ showBundle e Right x -> unless (p x) . expectationFailure $ "the value did not satisfy the predicate: " ++ show x -- | Check that a parser fails on a given input. -- -- > parse (char 'x') "" `shouldFailOn` "a" shouldFailOn :: (HasCallStack, Show a) => -- | Parser that takes stream and produces result or error message (s -> Either (ParseErrorBundle s e) a) -> -- | Input that the parser should fail on s -> Expectation p `shouldFailOn` s = shouldFail (p s) -- | Check that a parser succeeds on a given input. -- -- > parse (char 'x') "" `shouldSucceedOn` "x" shouldSucceedOn :: ( HasCallStack, ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s, Show a ) => -- | Parser that takes stream and produces result or error message (s -> Either (ParseErrorBundle s e) a) -> -- | Input that the parser should succeed on s -> Expectation p `shouldSucceedOn` s = shouldSucceed (p s) ---------------------------------------------------------------------------- -- Testing of error messages -- | Create an expectation that parser should fail producing certain -- 'ParseError'. Use the 'err' function from this module to construct a -- 'ParseError' to compare with. -- -- > parse (char 'x') "" "b" `shouldFailWith` err posI (utok 'b' <> etok 'x') shouldFailWith :: ( HasCallStack, ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s, Show a, Eq e ) => -- | The result of parsing Either (ParseErrorBundle s e) a -> -- | Expected parse errors ParseError s e -> Expectation r `shouldFailWith` perr1 = r `shouldFailWithM` [perr1] -- | Similar to 'shouldFailWith', but allows to check parsers that can -- report more than one parse error at a time. -- -- @since 2.0.0 shouldFailWithM :: ( HasCallStack, ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s, Show a, Eq e ) => -- | The result of parsing Either (ParseErrorBundle s e) a -> -- | Expected parse errors, the argument is a normal linked list (as -- opposed to the more correct 'NonEmpty' list) as a syntactical -- convenience for the user, passing empty list here will result in an -- error [ParseError s e] -> Expectation r `shouldFailWithM` perrs1' = case r of Left e0 -> let e1 = e0 {bundleErrors = perrs1} perrs0 = bundleErrors e0 perrs1 = NE.fromList perrs1' in unless (perrs0 == perrs1) . expectationFailure $ "the parser is expected to fail with:\n" ++ showBundle e1 ++ "but it failed with:\n" ++ showBundle e0 Right v -> expectationFailure $ "the parser is expected to fail, but it parsed: " ++ show v ---------------------------------------------------------------------------- -- Incremental parsing -- | Check that a parser fails and leaves a certain part of input -- unconsumed. Use it with functions like 'runParser'' and 'runParserT'' -- that support incremental parsing. -- -- > runParser' (many (char 'x') <* eof) (initialState "xxa") -- > `failsLeaving` "a" -- -- See also: 'initialState'. failsLeaving :: ( HasCallStack, Show a, Eq s, Show s ) => -- | Parser that takes stream and produces result along with actual -- state information (State s e, Either (ParseErrorBundle s e) a) -> -- | Part of input that should be left unconsumed s -> Expectation (st, r) `failsLeaving` s = do shouldFail r checkUnconsumed s (stateInput st) -- | Check that a parser succeeds and leaves certain part of input -- unconsumed. Use it with functions like 'runParser'' and 'runParserT'' -- that support incremental parsing. -- -- > runParser' (many (char 'x')) (initialState "xxa") -- > `succeedsLeaving` "a" -- -- See also: 'initialState'. succeedsLeaving :: ( HasCallStack, Show a, Eq s, Show s, ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s ) => -- | Parser that takes stream and produces result along with actual -- state information (State s e, Either (ParseErrorBundle s e) a) -> -- | Part of input that should be left unconsumed s -> Expectation (st, r) `succeedsLeaving` s = do shouldSucceed r checkUnconsumed s (stateInput st) -- | Given input for parsing, construct initial state for parser. initialState :: s -> State s e initialState s = State { stateInput = s, stateOffset = 0, statePosState = initialPosState s, stateParseErrors = [] } -- | Given input for parsing, construct initial positional state. -- -- @since 2.0.0 initialPosState :: s -> PosState s initialPosState s = PosState { pstateInput = s, pstateOffset = 0, pstateSourcePos = initialPos "", pstateTabWidth = defaultTabWidth, pstateLinePrefix = "" } ---------------------------------------------------------------------------- -- Helpers -- | Expect that the argument is a result of a failed parser. shouldFail :: (HasCallStack, Show a) => Either (ParseErrorBundle s e) a -> Expectation shouldFail r = case r of Left _ -> return () Right v -> expectationFailure $ "the parser is expected to fail, but it parsed: " ++ show v -- | Expectation that argument is result of a succeeded parser. shouldSucceed :: ( HasCallStack, ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s, Show a ) => Either (ParseErrorBundle s e) a -> Expectation shouldSucceed r = case r of Left e -> expectationFailure $ "the parser is expected to succeed, but it failed with:\n" ++ showBundle e Right _ -> return () -- | Compare two streams for equality and in the case of mismatch report it. checkUnconsumed :: ( HasCallStack, Eq s, Show s ) => -- | Expected unconsumed input s -> -- | Actual unconsumed input s -> Expectation checkUnconsumed e a = unless (e == a) . expectationFailure $ "the parser is expected to leave unconsumed input: " ++ show e ++ "\nbut it left this: " ++ show a -- | Render a parse error bundle in a way that is suitable for inserting it -- in a test suite report. showBundle :: ( ShowErrorComponent e, Stream s, VisualStream s, TraversableStream s ) => ParseErrorBundle s e -> String showBundle = unlines . fmap indent . lines . errorBundlePretty where indent x = if null x then x else " " ++ x hspec-megaparsec-2.2.0/hspec-megaparsec.cabal0000644000000000000000000000341207346545000017306 0ustar0000000000000000cabal-version: 1.18 name: hspec-megaparsec version: 2.2.0 license: BSD3 license-file: LICENSE.md maintainer: Mark Karpov author: Mark Karpov tested-with: ghc ==8.6.5 ghc ==8.8.4 ghc ==8.10.1 homepage: https://github.com/mrkkrp/hspec-megaparsec bug-reports: https://github.com/mrkkrp/hspec-megaparsec/issues synopsis: Utility functions for testing Megaparsec parsers with Hspec description: Utility functions for testing Megaparsec parsers with Hspec. category: Testing, Parsing build-type: Simple extra-doc-files: CHANGELOG.md README.md source-repository head type: git location: https://github.com/mrkkrp/hspec-megaparsec.git flag dev description: Turn on development settings. default: False manual: True library exposed-modules: Test.Hspec.Megaparsec default-language: Haskell2010 build-depends: base >=4.12 && <5.0, containers >=0.5 && <0.7, hspec-expectations >=0.8 && <0.9, megaparsec >=9.0 && <10.0 if flag(dev) ghc-options: -Wall -Werror else ghc-options: -Wall if flag(dev) ghc-options: -Wcompat -Wincomplete-record-updates -Wincomplete-uni-patterns -Wnoncanonical-monad-instances test-suite tests type: exitcode-stdio-1.0 main-is: Main.hs hs-source-dirs: tests default-language: Haskell2010 build-depends: base >=4.12 && <5.0, hspec >=2.0 && <3.0, hspec-expectations >=0.8 && <0.9, hspec-megaparsec -any, megaparsec >=9.0 && <10.0 if flag(dev) ghc-options: -Wall -Werror else ghc-options: -Wall hspec-megaparsec-2.2.0/tests/0000755000000000000000000000000007346545000014255 5ustar0000000000000000hspec-megaparsec-2.2.0/tests/Main.hs0000644000000000000000000000236007346545000015476 0ustar0000000000000000{-# LANGUAGE CPP #-} module Main (main) where import Data.Void import Test.Hspec import Test.Hspec.Megaparsec import Text.Megaparsec import Text.Megaparsec.Char #if !MIN_VERSION_base(4,13,0) import Data.Semigroup ((<>)) #endif type Parser = Parsec Void String -- | Toy tests, just an example of usage. main :: IO () main = hspec $ do describe "shouldParse" $ it "works" $ parse (letterChar :: Parser Char) "" "x" `shouldParse` 'x' describe "parseSatisfies" $ it "works" $ parse (many punctuationChar :: Parser String) "" "?!!" `parseSatisfies` ((== 3) . length) describe "shouldFailOn" $ it "works" $ parse (char 'x' :: Parser Char) "" `shouldFailOn` "a" describe "shouldSucceedOn" $ it "works" $ parse (char 'x' :: Parser Char) "" `shouldSucceedOn` "x" describe "shouldFailWith" $ it "works" $ parse (char 'x' :: Parser Char) "" "b" `shouldFailWith` err 0 (utok 'b' <> etok 'x') describe "failsLeaving" $ it "works" $ runParser' (many (char 'x') <* eof :: Parser String) (initialState "xxa") `failsLeaving` "a" describe "succeedsLeaving" $ it "works" $ runParser' (many (char 'x') :: Parser String) (initialState "xxa") `succeedsLeaving` "a"