packages feed

cabal-add-0.2.1: tests/Main.hs

{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultilineStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}

{- HLINT ignore "Avoid restricted function" -}

-- |
-- Copyright:   (c) 2023 Bodigrim
-- License:     BSD-3-Clause
module Main (main) where

import Data.Algorithm.Diff (Diff, PolyDiff (..), getDiff)
import Data.ByteString.Lazy (LazyByteString)
import Data.ByteString.Lazy.Char8 qualified as BL
import Data.Char (isAlpha)
import Data.Maybe (mapMaybe)
import Data.String (fromString)
import System.Directory (findExecutable)
import System.Exit (ExitCode (..))
import System.File.OsPath (readFile, writeFile)
import System.IO.Temp.OsPath (withSystemTempDirectory)
import System.IO.Unsafe (unsafePerformIO)
import System.OsPath (decodeFS, encodeFS, osp, (<.>), (</>))
import System.Process (cwd, env, proc, readCreateProcessWithExitCode)
import Test.Tasty (TestTree, defaultMain, testGroup)
import Test.Tasty.Providers (IsTest (..), singleTest, testFailed, testPassed)
import Prelude hiding (readFile, writeFile)

data CabalAddTest = CabalAddTest
  { catName :: String
  , catArgs :: [String]
  , catEnv :: [(String, String)]
  , catInput :: String
  , catOutput :: String
  }

instance IsTest CabalAddTest where
  testOptions = pure []
  run _opts CabalAddTest {..} _yieldProgress = do
    mCabalAddExe <- findExecutable "cabal-add"
    case mCabalAddExe of
      Nothing -> pure $ testFailed "cabal-add executable is not in PATH"
      Just cabalAddExe -> do
        let template = unsafePerformIO $ encodeFS $ map (\c -> if isAlpha c then c else '_') catName
        withSystemTempDirectory template $ \tempDir -> do
          let cabalFileName = tempDir </> template <.> [osp|cabal|]
          writeFile cabalFileName (fromString catInput)
          let crpr =
                (proc cabalAddExe catArgs)
                  { cwd = Just $ unsafePerformIO $ decodeFS tempDir
                  , env = Just catEnv
                  }
          (code, _out, err) <- readCreateProcessWithExitCode crpr ""
          case code of
            ExitFailure errCode -> pure $ testFailed $ "ExitFailure " ++ show errCode ++ "\n" ++ err
            ExitSuccess -> do
              output <- readFile cabalFileName
              let catOutput' = fromString catOutput
              pure $
                if BL.lines output == BL.lines catOutput'
                  then testPassed ""
                  else testFailed $ BL.unpack $ prettyDiff $ getDiff (BL.lines catOutput') (BL.lines output)

prettyDiff :: [Diff LazyByteString] -> LazyByteString
prettyDiff =
  BL.unlines
    . mapMaybe
      ( \case
          First xs -> Just $ BL.cons '-' xs
          Second ys -> Just $ BL.cons '+' ys
          Both xs _ -> Just $ BL.cons ' ' xs
      )

mkTest :: CabalAddTest -> TestTree
mkTest cat = singleTest (catName cat) cat

caseMultipleDependencies1 :: TestTree
caseMultipleDependencies1 =
  mkTest $
    CabalAddTest
      { catName = "add multiple dependencies 1"
      , catArgs = ["foo < 1 && >0.7", "baz ^>= 2.0"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:
              foo < 1 && >0.7,
              baz ^>= 2.0,
              base >=4.15 && <5
          """
      }

caseMultipleDependencies2 :: TestTree
caseMultipleDependencies2 =
  mkTest $
    CabalAddTest
      { catName = "add multiple dependencies 2"
      , catArgs = ["foo < 1 && >0.7", "baz ^>= 2.0"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends: base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends: foo < 1 && >0.7, baz ^>= 2.0, base >=4.15 && <5
          """
      }

caseMultipleDependencies3 :: TestTree
caseMultipleDependencies3 =
  mkTest $
    CabalAddTest
      { catName = "add multiple dependencies 3"
      , catArgs = ["foo < 1 && >0.7", "baz ^>= 2.0"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:   base >=4.15 && <5,
                             containers
          """
      , catOutput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:   foo < 1 && >0.7,
                             baz ^>= 2.0,
                             base >=4.15 && <5,
                             containers
          """
      }

caseMultipleDependencies4 :: TestTree
caseMultipleDependencies4 =
  mkTest $
    CabalAddTest
      { catName = "add multiple dependencies 4"
      , catArgs = ["foo < 1 && >0.7", "baz ^>= 2.0"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:   base >=4.15 && <5
                           , containers
                           , deepseq
          """
      , catOutput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:   foo < 1 && >0.7
                           , baz ^>= 2.0
                           , base >=4.15 && <5
                           , containers
                           , deepseq
          """
      }

caseLibraryInDescription :: TestTree
caseLibraryInDescription =
  mkTest $
    CabalAddTest
      { catName = "word 'library' in description"
      , catArgs = ["foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple
          description:
            A library of basic functionality.

          library
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple
          description:
            A library of basic functionality.

          library
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseImportFields1 :: TestTree
caseImportFields1 =
  mkTest $
    CabalAddTest
      { catName = "import fields 1"
      , catArgs = ["foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 2.2
          name:          dummy
          version:       0.13.0.0
          build-type:    Simple

          common foo
            build-depends: bar

          library
            import: foo
            exposed-modules: Foo
          """
      , catOutput =
          """
          cabal-version: 2.2
          name:          dummy
          version:       0.13.0.0
          build-type:    Simple

          common foo
            build-depends: bar

          library
            import: foo
            build-depends: foo < 1 && >0.7, quux < 1
            exposed-modules: Foo
          """
      }

caseImportFields2 :: TestTree
caseImportFields2 =
  mkTest $
    CabalAddTest
      { catName = "import fields 2"
      , catArgs = ["foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 2.2
          name:          dummy
          version:       0.13.0.0
          build-type:    Simple

          common foo
            build-depends: bar

          library
            Import : foo
            exposed-modules: Foo
          """
      , catOutput =
          """
          cabal-version: 2.2
          name:          dummy
          version:       0.13.0.0
          build-type:    Simple

          common foo
            build-depends: bar

          library
            Import : foo
            build-depends: foo < 1 && >0.7, quux < 1
            exposed-modules: Foo
          """
      }

caseImportFields3 :: TestTree
caseImportFields3 =
  mkTest $
    CabalAddTest
      { catName = "import fields 3"
      , catArgs = ["foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 2.2
          name:          dummy
          version:       0.13.0.0
          build-type:    Simple

          common foo
            build-depends: bar

          library
            Import : foo
          """
      , catOutput =
          """
          cabal-version: 2.2
          name:          dummy
          version:       0.13.0.0
          build-type:    Simple

          common foo
            build-depends: bar

          library
            Import : foo
            build-depends: foo < 1 && >0.7, quux < 1
          """
      }

caseSublibraryTarget1 :: TestTree
caseSublibraryTarget1 =
  mkTest $
    CabalAddTest
      { catName = "sublibrary target 1"
      , catArgs = ["foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          library baz
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          library baz
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseSublibraryTarget2 :: TestTree
caseSublibraryTarget2 =
  mkTest $
    CabalAddTest
      { catName = "sublibrary target 2"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:
              base >=4.15 && <5

          library baz
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:
              base >=4.15 && <5

          library baz
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseExecutableTarget1 :: TestTree
caseExecutableTarget1 =
  mkTest $
    CabalAddTest
      { catName = "executable target 1"
      , catArgs = ["exe", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable baz
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable baz
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseExecutableTarget2 :: TestTree
caseExecutableTarget2 =
  mkTest $
    CabalAddTest
      { catName = "executable target 2"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable baz
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable baz
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseExecutableTarget3 :: TestTree
caseExecutableTarget3 =
  mkTest $
    CabalAddTest
      { catName = "executable target 3"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable "baz"
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable "baz"
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseExecutableTarget4 :: TestTree
caseExecutableTarget4 =
  mkTest $
    CabalAddTest
      { catName = "executable target 4"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common baz
            language: Haskell2010

          executable baz
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common baz
            language: Haskell2010

          executable baz
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseTestTarget1 :: TestTree
caseTestTarget1 =
  mkTest $
    CabalAddTest
      { catName = "test target 1"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common baz
            language: Haskell2010

          test-suite baz
            type: exitcode-stdio-1.0
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common baz
            language: Haskell2010

          test-suite baz
            type: exitcode-stdio-1.0
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseTestTarget2 :: TestTree
caseTestTarget2 =
  mkTest $
    CabalAddTest
      { catName = "test target 2"
      , catArgs = ["test:baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common baz
            language: Haskell2010

          test-suite baz
            type: exitcode-stdio-1.0
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common baz
            language: Haskell2010

          test-suite baz
            type: exitcode-stdio-1.0
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseCommonStanzaTarget1 :: TestTree
caseCommonStanzaTarget1 =
  mkTest $
    CabalAddTest
      { catName = "common stanza as a target 1"
      , catArgs = ["foo", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common foo
            language: Haskell2010
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          common foo
            build-depends: foo < 1 && >0.7, quux < 1
            language: Haskell2010
          """
      }

caseCommonStanzaTarget2 :: TestTree
caseCommonStanzaTarget2 =
  mkTest $
    CabalAddTest
      { catName = "common stanza as a target 2"
      , catArgs = ["foo", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          Common    foo
            language: Haskell2010
            build-depends:
              , base
              , containers
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          Common    foo
            language: Haskell2010
            build-depends:
              , foo < 1 && >0.7
              , quux < 1
              , base
              , containers
          """
      }

caseTwoSpacesInStanza :: TestTree
caseTwoSpacesInStanza =
  mkTest $
    CabalAddTest
      { catName = "two spaces in stanza"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable  baz
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable  baz
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseTitleCaseStanza1 :: TestTree
caseTitleCaseStanza1 =
  mkTest $
    CabalAddTest
      { catName = "title case in stanza 1"
      , catArgs = ["foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          Library
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          Library
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseTitleCaseStanza2 :: TestTree
caseTitleCaseStanza2 =
  mkTest $
    CabalAddTest
      { catName = "title case in stanza 2"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          Executable baz
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          Executable baz
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseTitleCaseBuildDepends :: TestTree
caseTitleCaseBuildDepends =
  mkTest $
    CabalAddTest
      { catName = "title case in build-depends"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable baz
            main-is: Main.hs
            Build-Depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable baz
            main-is: Main.hs
            Build-Depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

caseSharedComponentPrefixes :: TestTree
caseSharedComponentPrefixes =
  mkTest $
    CabalAddTest
      { catName = "shared component prefixes"
      , catArgs = ["baz", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable bazzzy
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5

          executable baz
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.1.0.0
          cabal-version: 2.0
          build-type:    Simple

          executable bazzzy
            main-is: Main.hs
            build-depends:
              base >=4.15 && <5

          executable baz
            main-is: Main.hs
            build-depends:
              foo < 1 && >0.7,
              quux < 1,
              base >=4.15 && <5
          """
      }

windowsLineEndings :: TestTree
windowsLineEndings =
  mkTest $
    CabalAddTest
      { catName = "Windows line endings"
      , catArgs = ["exe", "foo < 1 && >0.7", "quux < 1"]
      , catEnv = mempty
      , catInput =
          convertToWindowsLineEndings
            """
            name:          dummy
            version:       0.1.0.0
            cabal-version: 2.0
            build-type:    Simple

            executable baz
              main-is: Main.hs
            """
      , catOutput =
          convertToWindowsLineEndings
            """
            name:          dummy
            version:       0.1.0.0
            cabal-version: 2.0
            build-type:    Simple

            executable baz
              build-depends: foo < 1 && >0.7, quux < 1
              main-is: Main.hs
            """
      }
  where
    convertToWindowsLineEndings :: String -> String
    convertToWindowsLineEndings = concatMap (\c -> if c == '\n' then ['\r', '\n'] else [c])

caseLeadingComma1 :: TestTree
caseLeadingComma1 =
  mkTest $
    CabalAddTest
      { catName = "build-depends start from comma 1"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends:
              , base >=4.15 && <5
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends:
              , baz ^>= 2.0
              , quux < 1
              , base >=4.15 && <5
          """
      }

caseLeadingComma2 :: TestTree
caseLeadingComma2 =
  mkTest $
    CabalAddTest
      { catName = "build-depends start from comma 2"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: ,base >=4.15 && <5
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: ,baz ^>= 2.0 ,quux < 1 ,base >=4.15 && <5
          """
      }

caseLeadingComma3 :: TestTree
caseLeadingComma3 =
  mkTest $
    CabalAddTest
      { catName = "build-depends start from comma 3"
      , catArgs = ["baz ^>= 2.0", "quux > 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: ,base >=4.15 && <5
                           ,containers
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: ,baz ^>= 2.0
                           ,quux > 1
                           ,base >=4.15 && <5
                           ,containers
          """
      }

caseConditionalBuildDepends :: TestTree
caseConditionalBuildDepends =
  mkTest $
    CabalAddTest
      { catName = "build-depends are under condition"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            if impl(ghc >= 9.6)
              build-depends:
                base >=4.15 && <5
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: baz ^>= 2.0, quux < 1
            if impl(ghc >= 9.6)
              build-depends:
                base >=4.15 && <5
          """
      }

caseEmptyComponent1 :: TestTree
caseEmptyComponent1 =
  mkTest $
    CabalAddTest
      { catName = "empty component 1"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library

          executable bar
            main-is: Bar.hs
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library

            build-depends: baz ^>= 2.0, quux < 1

          executable bar
            main-is: Bar.hs
          """
      }

caseEmptyComponent2 :: TestTree
caseEmptyComponent2 =
  mkTest $
    CabalAddTest
      { catName = "empty component 2"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: baz ^>= 2.0, quux < 1
          """
      }

caseEmptyComponent3 :: TestTree
caseEmptyComponent3 =
  mkTest $
    CabalAddTest
      { catName = "empty component 3"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library"""
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: baz ^>= 2.0, quux < 1
          """
      }

caseEmptyBuildDepends :: TestTree
caseEmptyBuildDepends =
  mkTest $
    CabalAddTest
      { catName = "empty build-depends"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends:
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          library
            build-depends: baz ^>= 2.0, quux < 1
            build-depends:
          """
      }

caseComponentInBraces :: TestTree
caseComponentInBraces =
  mkTest $
    CabalAddTest
      { catName = "component in figure braces"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          Library {
            Build-Depends: base >= 4 && < 5
          }
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          Library {
            Build-Depends: baz ^>= 2.0, quux < 1, base >= 4 && < 5
          }
          """
      }

caseCommentsWithCommas :: TestTree
caseCommentsWithCommas =
  mkTest $
    CabalAddTest
      { catName = "comments with commas"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            main-is:       Main.hs
            build-depends:    magda,
                              -- Something, which
                              -- contains commas.
                              base
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            main-is:       Main.hs
            build-depends:    baz ^>= 2.0, quux < 1, magda,
                              -- Something, which
                              -- contains commas.
                              base
          """
      }

caseCommentsWithoutCommas :: TestTree
caseCommentsWithoutCommas =
  mkTest $
    CabalAddTest
      { catName = "comments without commas"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            main-is:       Main.hs
            build-depends:  magda,
                            -- Something without commas
                            base
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            main-is:       Main.hs
            build-depends:  baz ^>= 2.0, quux < 1, magda,
                            -- Something without commas
                            base
          """
      }

caseDependenciesOnTheSameLine :: TestTree
caseDependenciesOnTheSameLine =
  mkTest $
    CabalAddTest
      { catName = "all build-deps on the same line"
      , catArgs = ["baz ^>= 2.0", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   magda, base
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   baz ^>= 2.0, quux < 1, magda, base
          """
      }

caseIgnoreAddArgument :: TestTree
caseIgnoreAddArgument =
  mkTest $
    CabalAddTest
      { catName = "ignore add as the first command-line argument"
      , catArgs = ["add", "baz ^>= 2.0", "quux < 1"]
      , catEnv = [("CABAL", "/fake/usr/bin/local/cabal")]
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   magda, base
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   baz ^>= 2.0, quux < 1, magda, base
          """
      }

caseDoNotIgnoreAddArgument :: TestTree
caseDoNotIgnoreAddArgument =
  mkTest $
    CabalAddTest
      { catName = "do not ignore add as the second command-line argument"
      , catArgs = ["baz ^>= 2.0", "add", "quux < 1"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   magda, base
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   baz ^>= 2.0, add, quux < 1, magda, base
          """
      }

caseSublibraryDependency :: TestTree
caseSublibraryDependency =
  mkTest $
    CabalAddTest
      { catName = "sublibrary as a dependency"
      , catArgs = ["foo:bar"]
      , catEnv = mempty
      , catInput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   magda, base
          """
      , catOutput =
          """
          cabal-version: 3.6
          name:          dummy
          version:       0.1
          build-type:    Simple

          executable dagda
            build-depends:   foo:bar, magda, base
          """
      }

caseSpecialDependencyName1 :: TestTree
caseSpecialDependencyName1 =
  mkTest $
    CabalAddTest
      { catName = "add dependency named 'add'"
      , catArgs = ["add"]
      , catEnv = mempty
      , catInput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:
              base >=4.15 && <5
          """
      , catOutput =
          """
          name:          dummy
          version:       0.13.0.0
          cabal-version: 2.0
          build-type:    Simple

          library
            build-depends:
              add,
              base >=4.15 && <5
          """
      }

main :: IO ()
main =
  defaultMain $
    testGroup
      "All"
      [ caseMultipleDependencies1
      , caseMultipleDependencies2
      , caseMultipleDependencies3
      , caseMultipleDependencies4
      , caseLibraryInDescription
      , caseImportFields1
      , caseImportFields2
      , caseImportFields3
      , caseSublibraryTarget1
      , caseSublibraryTarget2
      , caseExecutableTarget1
      , caseExecutableTarget2
      , caseExecutableTarget3
      , caseExecutableTarget4
      , caseTestTarget1
      , caseTestTarget2
      , caseCommonStanzaTarget1
      , caseCommonStanzaTarget2
      , caseTwoSpacesInStanza
      , caseTitleCaseStanza1
      , caseTitleCaseStanza2
      , caseTitleCaseBuildDepends
      , caseSharedComponentPrefixes
      , windowsLineEndings
      , caseLeadingComma1
      , caseLeadingComma2
      , caseLeadingComma3
      , caseConditionalBuildDepends
      , caseEmptyComponent1
      , caseEmptyComponent2
      , caseEmptyComponent3
      , caseEmptyBuildDepends
      , caseComponentInBraces
      , caseCommentsWithCommas
      , caseCommentsWithoutCommas
      , caseDependenciesOnTheSameLine
      , caseIgnoreAddArgument
      , caseDoNotIgnoreAddArgument
      , caseSublibraryDependency
      , caseSpecialDependencyName1
      ]