packages feed

cmake-syntax-0.1.0.0: test/Spec.hs

{-# LANGUAGE QuasiQuotes, OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-orphans #-}

import Data.Char
import Test.Hspec
import Text.RawString.QQ
import Text.Trifecta.Parser
import Text.Trifecta.Result

import Language.CMake.AST
import Language.CMake.Parser

instance Eq a => Eq (Result a) where
  Success a == Success b = a == b
  Failure _ == Failure _ = True
  _ == _ = False

(~~>) :: HasCallStack => String -> File -> Expectation
(~~>) str res = parseString fileParser mempty (str <> "\n") `shouldBe` Success res

dropLeading :: String -> String
dropLeading str = init $ unlines $ l : (drop numSpaces <$> init ls)
  where
    (l:ls) = lines str
    numSpaces = length $ takeWhile isSpace $ last ls

main :: IO ()
main = hspec $ do
  describe "Parsing simple commands" $ do
    it "without args"
        $ [r|add_executable()|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" [] ]
    it "with leading spaces"
        $ [r|    add_executable()|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" [] ]
    it "empty line"
        $ [r||]
      ~~> File [ NonCommandElement ]
    it "empty line with spaces"
        $ "   \t  "
      ~~> File [ NonCommandElement ]
  describe "Parsing a sequence of commands" $ do
    it "without args"
        $ [r|add_executable()
             add_library()|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" []
               , CommandElement $ CommandInvocation "add_library" []
               ]
    it "with empty lines"
        $ [r|add_executable()

             add_library()|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" []
               , NonCommandElement
               , CommandElement $ CommandInvocation "add_library" []
               ]
  describe "Parsing comments" $ do
    it "parses single whole-line comment"
        $ [r|# this is a comment|]
      ~~> File [NonCommandElement]
    it "parses multiple whole-line comments"
        $ [r|# this is a comment
             # and this is a continuation|]
      ~~> File [NonCommandElement, NonCommandElement]
    it "parses end-of-line comments"
        $ [r|add_library() # this is a comment|]
      ~~> File [CommandElement $ CommandInvocation "add_library" []]
  describe "Bracket comments" $ do
    it "parses simplest empty bracket comments"
        $ [r|#[[]]|]
      ~~> File [NonCommandElement]
    it "parses zero-eq bracket comments with text"
        $ [r|#[[ foobar ]]|]
      ~~> File [NonCommandElement]
    it "parses non-zero-eq bracket comments with text"
        $ [r|#[==[ foobar ]==]|]
      ~~> File [NonCommandElement]
    it "parses non-zero-eq bracket comments with nested eqs (less)"
        $ [r|#[==[ ]=] ]==]|]
      ~~> File [NonCommandElement]
    it "parses non-zero-eq bracket comments with nested eqs (more)"
        $ [r|#[==[ ]===] ]==]|]
      ~~> File [NonCommandElement]
    it "parses multilinenon-zero-eq bracket comments"
        $ [r|#[==[ foobar
             ]=]
             fdsafsdahljkrew hjkfdshafkjlashjk
             ]===]
             ]==]|]
      ~~> File [NonCommandElement]
  describe "Parsing simple command arguments" $ do
    it "parses command with one arg"
        $ [r|add_executable(foo)|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo"] ]
    it "parses command with several simple args"
        $ [r|add_executable(foo bar baz)|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo", "bar", "baz"] ]
  describe "Parsing bracket command arguments" $ do
    it "parses command with bracket arguments"
        $ dropLeading
          [r|add_executable([==[
            This is a bracket argument
            ]==])
            |]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is a bracket argument\n"] ]
    it "parses command with multiline bracket arguments"
        $ dropLeading
          [r|add_executable([==[
            This is a
            bracket
            argument
            ]==])
            |]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is a\nbracket\nargument\n"] ]
    it "does not escape in bracket arguments"
        $ dropLeading
          [r|add_executable([==[
            This is \t \; \n a \- bracket argument \meh
            ]==])
            |]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is \\t \\; \\n a \\- bracket argument \\meh\n"] ]
    it "does not expand variables in bracket arguments"
        $ dropLeading
          [r|add_executable([==[
            This is a ${bracket} argument
            ]==])
            |]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["\nThis is a ${bracket} argument\n"] ]
  describe "Parsing quoted command arguments" $ do
    it "parses command with one arg"
        $ [r|add_executable("foo")|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo"] ]
    it "parses command with multple args"
        $ [r|add_executable("foo" "bar")|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo", "bar"] ]
    it "does escaping"
        $ [r|add_executable("foo \n \; bar")|]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo \n ; bar"] ]
    it "parses command with multiline arg"
        $ dropLeading
          [r|add_executable("foo
             bar
             baz")
             |]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo\nbar\nbaz"] ]
    it "parses command with multiline arg and final line feed"
        $ dropLeading
          [r|add_executable("foo
             bar
             baz
             ")
             |]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo\nbar\nbaz\n"] ]
    it "parses command with single-line arg spanning multiple lines"
        $ dropLeading
          [r|add_executable("foo \
             bar \
             baz")
             |]
      ~~> File [ CommandElement $ CommandInvocation "add_executable" ["foo bar baz"] ]