File: OptionsSpec.hs

package info (click to toggle)
bnfc 2.9.6.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,300 kB
  • sloc: haskell: 16,920; yacc: 240; makefile: 91
file content (137 lines) | stat: -rw-r--r-- 4,807 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

module BNFC.OptionsSpec where

import Control.Monad.Writer (WriterT(..))
import Data.List            (intercalate)
import System.FilePath      ((<.>), takeBaseName)

import Test.Hspec
import Test.QuickCheck

import BNFC.Options


-- Expectation that a particular option has a particular value
shouldSet :: (Eq a, Show a) => Mode -> (SharedOptions -> a, a) -> Expectation
shouldSet (Target opts _) (option, value) = option opts `shouldBe` value

spec :: Spec
spec = do

  describe "parseMode" $ do

    it "returns Help on an empty list of arguments" $
      parseMode_ [] `shouldBe` Help

    it "returns Help if given --help" $
      parseMode_ ["--help"] `shouldBe` Help

    it "returns Version if given --version" $
      parseMode_ ["--version"] `shouldBe` Version

    it "returns an error if help is given an argument" $
      isUsageError (parseMode_ ["--help=2"]) `shouldBe` True

    it "If no language is specified, it should default to haskell" $
      parseMode_ ["file.cf"] `shouldSet` (target, TargetHaskell)

    it "returns an error if the grammar file is missing" $
      parseMode_ ["--haskell"] `shouldBe` UsageError "Missing grammar file"

    it "returns an error if multiple grammar files are given" $
      parseMode_ ["--haskell", "file1.cf", "file2.cf"]
        `shouldBe` UsageError "Too many arguments"

    it "sets the language name to the basename of the grammar file" $
      parseMode_ ["foo.cf"] `shouldSet` (lang, "foo")

    it "accept 'old style' options" $ do
      parseMode_ ["-haskell", "-m", "-glr", "file.cf"]
        `shouldSet` (target, TargetHaskell)
      parseMode_ ["-haskell", "-m", "-glr", "file.cf"]
        `shouldSet` (optMake, Just "Makefile")
      parseMode_ ["-haskell", "-m", "-glr", "file.cf"]
        `shouldSet` (glr, GLR)

  it "accept latex as a target language" $
    parseMode_ ["--latex", "file.cf"] `shouldSet` (target, TargetLatex)

  describe "Old option translation" $ do
    it "translate -haskell to --haskell" $
      translateOldOptions ["-haskell"] `shouldBe`
        (WriterT $ Right (["--haskell"]
                         ,["Warning: unrecognized option -haskell treated as if --haskell was provided."]))

    describe "--makefile" $ do

      it "is off by default" $
        parseMode_ ["--c", "foo.cf"] `shouldSet` (optMake, Nothing)

      it "uses the file name 'Makefile' by default" $
        parseMode_ ["--c", "-m", "foo.cf"] `shouldSet` (optMake, Just "Makefile")

      context "when using the option with an argument" $
        it "uses the argument as Makefile name" $
          parseMode_ ["--c", "-mMyMakefile", "foo.cf"]
            `shouldSet` (optMake, Just "MyMakefile")
  where
  parseMode_ = fst . parseMode

isUsageError :: Mode -> Bool
isUsageError = \case
  UsageError{} -> True
  _ -> False

-- ~~~ Arbitrary instances ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

randomOption :: Gen String
randomOption = oneof [ nonOption, noArg, withArg ]
  where nonOption = stringOf1 ['a'..'z'] -- non-option argument
        noArg     = ("--"++) <$> nonOption -- flag
        withArg   = do
          arg   <- nonOption
          flag  <- noArg
          return $ flag ++ "=" ++ arg

-- Helper function that generates a string of random length using the given
-- set of characters. Not that the type signature explicitely uses
-- [Char] and not String for documentation purposes
stringOf :: [Char] -> Gen String
stringOf = listOf . elements

-- | Same as stringOf but only generates non empty strings
stringOf1 :: [Char] -> Gen String
stringOf1 = listOf1 . elements


instance Arbitrary Target where
  arbitrary = elements [minBound .. maxBound]

-- creates a filepath with the given extension
arbitraryFilePath :: String -> Gen FilePath
arbitraryFilePath ext = do
  path <- listOf1 $ stringOf1 ['a'..'z']
  return $ intercalate "/" path <.> ext

-- Generates unix command line options. Can be in long form (ex: --option)
-- or short form (ex: -o)
-- Note: we only use letters x,y,z to make (almost) sure that we are not
-- going to generate accidentally an global/target language option
arbitraryOption :: Gen String
arbitraryOption = oneof [arbitraryShortOption, arbitraryLongOption]
  where
  arbitraryShortOption = ('-':) . (:[]) <$> elements ['x'..'z']
  arbitraryLongOption  = ("--" ++)      <$> stringOf1 ['x'..'z']

-- Arbitrary instance for Mode
instance Arbitrary Mode where
  arbitrary = oneof
    [ return Help
    , return Version
    , UsageError <$> arbitrary          -- generates a random error message
    , do target' <- arbitrary              -- random target
         cfFile <- arbitraryFilePath "cf"
         let args = defaultOptions { lang = takeBaseName cfFile, target = target'}
         return $ Target args cfFile
    ]