<NME> EndToEndSpec.hs
<BEF> {-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ConstraintKinds #-}
module EndToEndSpec (spec) where

import           Prelude hiding (writeFile)
import qualified Prelude

import           Helper
import           Test.HUnit

import           System.Directory (canonicalizePath)
import           Data.Maybe
import           Data.List
import           Data.String.Interpolate
import           Data.String.Interpolate.Util
import           Data.Version (showVersion)

import qualified Hpack.Render as Hpack
import           Hpack.Config (packageConfig, readPackageConfig, DecodeOptions(..), DecodeResult(..), defaultDecodeOptions)
import           Hpack.Render.Hints (FormattingHints(..), sniffFormattingHints)

import qualified Paths_hpack as Hpack (version)

writeFile :: FilePath -> String -> IO ()
writeFile file c = touch file >> Prelude.writeFile file c

spec :: Spec
spec = around_ (inTempDirectoryNamed "foo") $ do
  describe "hpack" $ do
    it "ignores fields that start with an underscore" $ do
      [i|
      _foo:
        bar: 23
      library: {}
      |] `shouldRenderTo` library_ [i|
      |]
    it "warns on duplicate fields" $ do
      [i|
      name: foo
      name: foo
      |] `shouldWarn` [
          "package.yaml: Duplicate field $.name"
        ]

    describe "tested-with" $ do
      it "accepts a string" $ do
        [i|
        tested-with: GHC == 7.0.4
        |] `shouldRenderTo` package [i|
        tested-with:
            GHC == 7.0.4
        |]

      it "accepts a list" $ do
        [i|
        tested-with:
        - GHC == 7.0.4
        - GHC == 7.2.2
        - GHC == 7.4.2
        |] `shouldRenderTo` package [i|
        tested-with:
            GHC == 7.0.4
          , GHC == 7.2.2
          , GHC == 7.4.2
        |]

    describe "handling of Paths_ module" $ do
      it "adds Paths_ to other-modules" $ do
        [i|
        library: {}
        |] `shouldRenderTo` library [i|
        other-modules:
            Paths_foo
        default-language: Haskell2010
        |]

      context "when cabal-version is >= 2" $ do
        it "adds Paths_ to autogen-modules" $ do
          [i|
          verbatim:
            cabal-version: 2.0
          library: {}
          |] `shouldRenderTo` (library [i|
          other-modules:
              Paths_foo
          autogen-modules:
              Paths_foo
          default-language: Haskell2010
          |]) { packageCabalVersion = "2.0" }

        context "when Paths_ module is listed explicitly under generated-other-modules" $ do
          it "adds Paths_ to autogen-modules only once" $ do
            [i|
            verbatim:
              cabal-version: 2.0
            library:
              generated-other-modules: Paths_foo
            |] `shouldRenderTo` (library [i|
            other-modules:
                Paths_foo
            autogen-modules:
                Paths_foo
            default-language: Haskell2010
            |]) { packageCabalVersion = "2.0" }

        context "when Paths_ module is listed explicitly under generated-exposed-modules" $ do
          it "adds Paths_ to autogen-modules only once" $ do
            [i|
            verbatim:
              cabal-version: 2.0
            library:
              generated-exposed-modules: Paths_foo
            |] `shouldRenderTo` (library [i|
            exposed-modules:
                Paths_foo
            autogen-modules:
                Paths_foo
            default-language: Haskell2010
            |]) { packageCabalVersion = "2.0" }

      context "when Paths_ is mentioned in a conditional that is always false" $ do
        it "does not add Paths_" $ do
          [i|
          library:
            when:
            - condition: false
              other-modules: Paths_foo
          |] `shouldRenderTo` library [i|
          default-language: Haskell2010
          |]

      context "when Paths_ is used with RebindableSyntax and (OverloadedStrings or OverloadedLists)" $ do
        it "infers cabal-version 2.2" $ do
          [i|
          default-extensions: [RebindableSyntax, OverloadedStrings]
          library: {}
          |] `shouldRenderTo` (library [i|
          default-extensions:
              RebindableSyntax
              OverloadedStrings
          other-modules:
              Paths_foo
          autogen-modules:
              Paths_foo
          default-language: Haskell2010
          |]) {packageCabalVersion = "2.2"}

        context "when Paths_ is mentioned in a conditional that is always false" $ do
          it "does not infer cabal-version 2.2" $ do
            [i|
            default-extensions: [RebindableSyntax, OverloadedStrings]
            library:
              when:
              - condition: false
                other-modules: Paths_foo
            |] `shouldRenderTo` (library [i|
            default-extensions:
                RebindableSyntax
                OverloadedStrings
            default-language: Haskell2010
            |])

    describe "spec-version" $ do
      it "accepts spec-version" $ do
        [i|
        spec-version: 0.29.5
        |] `shouldRenderTo` package [i|
        |]

      it "fails on malformed spec-version" $ do
        [i|
        spec-version: foo
        |] `shouldFailWith` "package.yaml: Error while parsing $.spec-version - invalid value \"foo\""

      it "fails on unsupported spec-version" $ do
        [i|
        spec-version: 25.0
        |] `shouldFailWith` ("The file package.yaml requires version 25.0 of the Hpack package specification, however this version of hpack only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of hpack may resolve this issue.")

      it "fails on unsupported spec-version from defaults" $ do
        let file = joinPath ["defaults", "sol", "hpack-template", "2017", "defaults.yaml"]
        writeFile file [i|
        spec-version: 25.0
        |]

        [i|
        defaults:
          github: sol/hpack-template
          path: defaults.yaml
          ref: "2017"
        library: {}
        |] `shouldFailWith` ("The file " ++ file ++ " requires version 25.0 of the Hpack package specification, however this version of hpack only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of hpack may resolve this issue.")

    describe "data-files" $ do
      it "accepts data-files" $ do
        touch "data/foo/index.html"
        touch "data/bar/index.html"
        [i|
        data-files:
          - data/**/*.html
        |] `shouldRenderTo` package [i|
        data-files:
            data/bar/index.html
            data/foo/index.html
        |]

    describe "data-dir" $ do
      it "accepts data-dir" $ do
        touch "data/foo.html"
        touch "data/bar.html"
        [i|
        data-dir: data
        data-files:
          - "*.html"
        |] `shouldRenderTo` package [i|
        data-files:
            bar.html
            foo.html
        data-dir: data
        |]

    describe "github" $ do
      it "accepts owner/repo" $ do
        [i|
        github: sol/hpack
        |] `shouldRenderTo` package [i|
        homepage: https://github.com/sol/hpack#readme
        bug-reports: https://github.com/sol/hpack/issues
        source-repository head
          type: git
          location: https://github.com/sol/hpack
        |]

      it "accepts owner/repo/path" $ do
        [i|
        github: hspec/hspec/hspec-core
        |] `shouldRenderTo` package [i|
        homepage: https://github.com/hspec/hspec#readme
        bug-reports: https://github.com/hspec/hspec/issues
        source-repository head
          type: git
          location: https://github.com/hspec/hspec
          subdir: hspec-core
        |]

      it "rejects URLs" $ do
        [i|
        github: https://github.com/sol/hpack/issues/365
        |] `shouldFailWith` "package.yaml: Error while parsing $.github - expected owner/repo or owner/repo/subdir, but encountered \"https://github.com/sol/hpack/issues/365\""

    describe "homepage" $ do
      it "accepts homepage URL" $ do
        [i|
        homepage: https://example.com/
        |] `shouldRenderTo` package [i|
        homepage: https://example.com/
        |]

      context "with github" $ do
        it "gives homepage URL precedence" $ do
          [i|
          github: hspec/hspec
          homepage: https://example.com/
          |] `shouldRenderTo` package [i|
          homepage: https://example.com/
          bug-reports: https://github.com/hspec/hspec/issues
          source-repository head
            type: git
            location: https://github.com/hspec/hspec
          |]

        it "omits homepage URL if it is null" $ do
          [i|
          github: hspec/hspec
          homepage: null
          |] `shouldRenderTo` package [i|
          bug-reports: https://github.com/hspec/hspec/issues
          source-repository head
            type: git
            location: https://github.com/hspec/hspec
          |]

    describe "bug-reports" $ do
      it "accepts bug-reports URL" $ do
        [i|
        bug-reports: https://example.com/
        |] `shouldRenderTo` package [i|
        bug-reports: https://example.com/
        |]

      context "with github" $ do
        it "gives bug-reports URL precedence" $ do
          [i|
          github: hspec/hspec
          bug-reports: https://example.com/
          |] `shouldRenderTo` package [i|
          homepage: https://github.com/hspec/hspec#readme
          bug-reports: https://example.com/
          source-repository head
            type: git
            location: https://github.com/hspec/hspec
          |]

        it "omits bug-reports URL if it is null" $ do
          [i|
          github: hspec/hspec
          bug-reports: null
          |] `shouldRenderTo` package [i|
          homepage: https://github.com/hspec/hspec#readme
          source-repository head
            type: git
            location: https://github.com/hspec/hspec
          |]

    describe "defaults" $ do
      it "accepts global defaults" $ do
        writeFile "defaults/sol/hpack-template/2017/defaults.yaml" [i|
        default-extensions:
          - RecordWildCards
          - DeriveFunctor
        |]

        [i|
        defaults:
          github: sol/hpack-template
          path: defaults.yaml
          ref: "2017"
        library: {}
        |] `shouldRenderTo` library_ [i|
        default-extensions:
            RecordWildCards
            DeriveFunctor
        |]

      it "accepts library defaults" $ do
        writeFile "defaults/sol/hpack-template/2017/defaults.yaml" [i|
        exposed-modules: Foo
        |]

        [i|
        library:
          defaults:
            github: sol/hpack-template
            path: defaults.yaml
            ref: "2017"
        |] `shouldRenderTo` library_ [i|
        exposed-modules:
            Foo
        |]

      it "accepts executable defaults" $ do
        writeFile "defaults/sol/hpack-template/2017/.hpack/defaults.yaml" [i|
        main: Foo.hs
        |]

        [i|
        executable:
          defaults: sol/hpack-template@2017
        |] `shouldRenderTo` executable_ "foo" [i|
        main-is: Foo.hs
        |]

      it "gives `main` from executable section precedence" $ do
        writeFile "defaults/sol/hpack-template/2017/.hpack/defaults.yaml" [i|
        main: Foo.hs
        |]

        [i|
        executable:
          main: Bar.hs
          defaults: sol/hpack-template@2017
        |] `shouldRenderTo` executable_ "foo" [i|
        main-is: Bar.hs
        |]

      it "accepts a list of defaults" $ do
        writeFile "defaults/foo/bar/v1/.hpack/defaults.yaml" "default-extensions: RecordWildCards"
        writeFile "defaults/foo/bar/v2/.hpack/defaults.yaml" "default-extensions: DeriveFunctor"
        [i|
        defaults:
          - foo/bar@v1
          - foo/bar@v2
        library: {}
        |] `shouldRenderTo` library_ [i|
        default-extensions:
            RecordWildCards
            DeriveFunctor
        |]

      it "accepts defaults recursively" $ do
        writeFile "defaults/foo/bar/v1/.hpack/defaults.yaml" "defaults: foo/bar@v2"
        writeFile "defaults/foo/bar/v2/.hpack/defaults.yaml" "default-extensions: DeriveFunctor"
        [i|
        defaults: foo/bar@v1
        library: {}
        |] `shouldRenderTo` library_ [i|
        default-extensions:
            DeriveFunctor
        |]

      it "fails on cyclic defaults" $ do
        let
          file1 = "defaults/foo/bar/v1/.hpack/defaults.yaml"
          file2 = "defaults/foo/bar/v2/.hpack/defaults.yaml"
        writeFile file1 "defaults: foo/bar@v2"
        writeFile file2 "defaults: foo/bar@v1"
        canonic1 <- canonicalizePath file1
        canonic2 <- canonicalizePath file2
        [i|
        defaults: foo/bar@v1
        library: {}
        |] `shouldFailWith` [i|cycle in defaults (#{canonic1} -> #{canonic2} -> #{canonic1})|]

      it "fails if defaults don't exist" $ do
        pending
        [i|
        defaults:
          github: sol/foo
          ref: bar
        library: {}
        |] `shouldFailWith` "Invalid value for \"defaults\"! File https://raw.githubusercontent.com/sol/foo/bar/.hpack/defaults.yaml does not exist!"

      it "fails on parse error" $ do
        let file = joinPath ["defaults", "sol", "hpack-template", "2017", "defaults.yaml"]
        writeFile file "[]"
        [i|
        defaults:
          github: sol/hpack-template
          path: defaults.yaml
          ref: "2017"
        library: {}
        |] `shouldFailWith` (file ++ ": Error while parsing $ - expected Object, but encountered Array")

      it "warns on unknown fields" $ do
        let file = joinPath ["defaults", "sol", "hpack-template", "2017", "defaults.yaml"]
        writeFile file "foo: bar"
        [i|
        name: foo
        defaults:
          github: sol/hpack-template
          path: defaults.yaml
          ref: "2017"
          bar: baz
        library: {}
        |] `shouldWarn` [
            "package.yaml: Ignoring unrecognized field $.defaults.bar"
          , file ++ ": Ignoring unrecognized field $.foo"
          ]

      it "accepts defaults from local files" $ do
        writeFile "defaults/foo.yaml" [i|
        defaults:
          local: bar.yaml
        |]

        writeFile "defaults/bar.yaml" [i|
        default-extensions:
          - RecordWildCards
          - DeriveFunctor
        |]

        [i|
        defaults:
          local: defaults/foo.yaml
        library: {}
        |] `shouldRenderTo` library_ [i|
        default-extensions:
            RecordWildCards
            DeriveFunctor
        |]

    describe "version" $ do
      it "accepts string" $ do
        [i|
        version: 0.1.0
        |] `shouldRenderTo` (package "") {packageVersion = "0.1.0"}

      it "accepts number" $ do
        [i|
        version: 0.1
        |] `shouldRenderTo` (package [i|
        |]) {packageVersion = "0.1"}

      it "rejects other values" $ do
        [i|
        version: {}
        |] `shouldFailWith` "package.yaml: Error while parsing $.version - expected Number or String, but encountered Object"

    describe "license" $ do
      it "accepts cabal-style licenses" $ do
        [i|
        license: BSD3
        |] `shouldRenderTo` (package [i|
        license: BSD3
        |])

      it "accepts SPDX licenses" $ do
        [i|
        license: BSD-3-Clause
        |] `shouldRenderTo` (package [i|
        license: BSD-3-Clause
        |]) {packageCabalVersion = "2.2"}

      context "with an ambiguous license" $ do
        it "treats it as a cabal-style license" $ do
          [i|
          license: MIT
          |] `shouldRenderTo` (package [i|
          license: MIT
          |])

      context "when cabal-version >= 2.2" $ do
        it "maps license to SPDX license identifier" $ do
          [i|
          license: BSD3
          library:
            cxx-options: -Wall
          |] `shouldRenderTo` (package [i|
          license: BSD-3-Clause
          library
            other-modules:
                Paths_foo
            autogen-modules:
                Paths_foo
            cxx-options: -Wall
            default-language: Haskell2010
          |]) {packageCabalVersion = "2.2"}

        it "doesn't touch unknown licenses" $ do
          [i|
          license: some-license
          library:
            cxx-options: -Wall
          |] `shouldRenderTo` (package [i|
          license: some-license
          library
            other-modules:
                Paths_foo
            autogen-modules:
                Paths_foo
            cxx-options: -Wall
            default-language: Haskell2010
          |]) {packageCabalVersion = "2.2"}

      context "with a LICENSE file" $ do
        before_ (writeFile "LICENSE" license) $ do
          it "infers license" $ do
            [i|
            |] `shouldRenderTo` (package [i|
            license-file: LICENSE
            license: MIT
            |])

          context "when license can not be inferred" $ do
            it "warns" $ do
              writeFile "LICENSE" "some-licenese"
              [i|
              name: foo
              |] `shouldWarn` ["Inferring license from file LICENSE failed!"]

          context "when license is null" $ do
            it "does not infer license" $ do
              [i|
              license: null
              |] `shouldRenderTo` (package [i|
              license-file: LICENSE
              |])

    describe "build-type" $ do
      it "accept Simple" $ do
        [i|
        build-type: Simple
        |] `shouldRenderTo` (package "") {packageBuildType = "Simple"}

      it "accept Configure" $ do
        [i|
        build-type: Configure
        |] `shouldRenderTo` (package "") {packageBuildType = "Configure"}

      it "accept Make" $ do
        [i|
        build-type: Make
        |] `shouldRenderTo` (package "") {packageBuildType = "Make"}

      it "accept Custom" $ do
        [i|
        build-type: Custom
        |] `shouldRenderTo` (package "") {packageBuildType = "Custom"}

      it "rejects invalid values" $ do
        [i|
        build-type: foo
        |] `shouldFailWith` "package.yaml: Error while parsing $.build-type - expected one of Simple, Configure, Make, or Custom"


    describe "extra-doc-files" $ do
      it "accepts a list of files" $ do
        touch "CHANGES.markdown"
        touch "README.markdown"
        [i|
        extra-doc-files:
          - CHANGES.markdown
          - README.markdown
        |] `shouldRenderTo` (package [i|
        extra-doc-files:
            CHANGES.markdown
            README.markdown
        |]) {packageCabalVersion = "1.18"}

      it "accepts glob patterns" $ do
        touch "CHANGES.markdown"
        touch "README.markdown"
        [i|
        extra-doc-files:
          - "*.markdown"
        |] `shouldRenderTo` (package [i|
        extra-doc-files:
            CHANGES.markdown
            README.markdown
        |]) {packageCabalVersion = "1.18"}

      it "warns if a glob pattern does not match anything" $ do
        [i|
        name: foo
        extra-doc-files:
          - "*.markdown"
        |] `shouldWarn` ["Specified pattern \"*.markdown\" for extra-doc-files does not match any files"]

    describe "build-tools" $ do
      it "adds known build tools to build-tools" $ do
        [i|
        executable:
          build-tools:
            alex == 0.1.0
        |] `shouldRenderTo` executable_ "foo" [i|
        build-tools:
            alex ==0.1.0
        |]

      it "adds other build tools to build-tool-depends" $ do
        [i|
        executable:
          build-tools:
            hspec-discover: 0.1.0
        |] `shouldRenderTo` (executable_ "foo" [i|
        build-tool-depends:
            hspec-discover:hspec-discover ==0.1.0
        |]) {
          -- NOTE: We do not set this to 2.0 on purpose, so that the .cabal
          -- file is compatible with a wider range of Cabal versions!
          packageCabalVersion = "1.12"
        }

      it "accepts build-tool-depends as an alias" $ do
        [i|
        executable:
          build-tool-depends:
            hspec-discover: 0.1.0
        |] `shouldRenderTo` (executable_ "foo" [i|
        build-tool-depends:
            hspec-discover:hspec-discover ==0.1.0
        |]) {
          packageCabalVersion = "1.12"
        , packageWarnings = ["package.yaml: $.executable.build-tool-depends is deprecated, use $.executable.build-tools instead"]
        }

      context "when the name of a build tool matches an executable from the same package" $ do
        it "adds it to build-tools" $ do
          [i|
          executables:
            bar:
              build-tools:
                - bar
          |] `shouldRenderTo` executable_ "bar" [i|
          build-tools:
              bar
          |]

        it "gives per-section unqualified names precedence over global qualified names" $ do
          [i|
          build-tools:
            - foo:bar == 0.1.0
          executables:
            bar:
              build-tools:
                - bar == 0.2.0
          |] `shouldRenderTo` executable_ "bar" [i|
          build-tools:
              bar ==0.2.0
          |]

        it "gives per-section qualified names precedence over global unqualified names" $ do
          [i|
          build-tools:
            - bar == 0.1.0
          executables:
            bar:
              build-tools:
                - foo:bar == 0.2.0
          |] `shouldRenderTo` executable_ "bar" [i|
          build-tools:
              bar ==0.2.0
          |]

      context "when the name of a build tool matches a legacy system build tool" $ do
        it "adds it to build-tools" $ do
          [i|
          executable:
            build-tools:
              ghc >= 7.10
          |] `shouldRenderTo` (executable_ "foo" [i|
          build-tools:
              ghc >=7.10
          |]) { packageWarnings = ["Listing \"ghc\" under build-tools is deperecated! Please list system executables under system-build-tools instead!"] }

    describe "system-build-tools" $ do
      it "adds system build tools to build-tools" $ do
        [i|
        executable:
          system-build-tools:
            ghc >= 7.10
        |] `shouldRenderTo` executable_ "foo" [i|
        build-tools:
            ghc >=7.10
        |]

      context "with hpc" $ do
        it "infers cabal-version 1.14" $ do
          [i|
          executable:
            system-build-tools:
              hpc
          |] `shouldRenderTo` (executable_ "foo" [i|
          build-tools:
              hpc
          |]) {packageCabalVersion = "1.14"}

      context "with ghcjs" $ do
        it "infers cabal-version 1.22" $ do
          [i|
          executable:
            system-build-tools:
              ghcjs
          |] `shouldRenderTo` (executable_ "foo" [i|
          build-tools:
              ghcjs
          |]) {packageCabalVersion = "1.22"}

      context "with an unknown system build tool" $ do
        it "infers cabal-version 2.0" $ do
          [i|
          executable:
            system-build-tools:
              g++ >= 5.4.0
          |] `shouldRenderTo` (executable_ "foo" [i|
          autogen-modules:
              Paths_foo
          build-tools:
              g++ >=5.4.0
          |]) {packageCabalVersion = "2.0"}

    describe "dependencies" $ do
      it "accepts single dependency" $ do
        [i|
        executable:
          dependencies: base
        |] `shouldRenderTo` executable_ "foo" [i|
        build-depends:
            base
        |]

      it "accepts build-depends as an alias" $ do
        [i|
        executable:
          build-depends: base
        |] `shouldRenderTo` (executable_ "foo" [i|
        build-depends:
            base
        |]) {
          packageWarnings = ["package.yaml: $.executable.build-depends is deprecated, use $.executable.dependencies instead"]
        }

      it "accepts dependencies with subcomponents" $ do
        [i|
        executable:
          dependencies: foo:bar
        |] `shouldRenderTo` (executable_ "foo" [i|
        autogen-modules:
            Paths_foo
        build-depends:
            foo:bar
        |]) {packageCabalVersion = "3.0"}

      it "accepts list of dependencies" $ do
        [i|
        executable:
          dependencies:
            - base
            - transformers
        |] `shouldRenderTo` executable_ "foo" [i|
        build-depends:
            base
          , transformers
        |]

      context "with both global and section specific dependencies" $ do
        it "combines dependencies" $ do
          [i|
          dependencies:
            - base
          executable:
            dependencies: hspec
          |] `shouldRenderTo` executable_ "foo" [i|
          build-depends:
              base
            , hspec
          |]

        it "gives section specific dependencies precedence" $ do
          [i|
          dependencies:
            - base
          executable:
            dependencies: base >= 2
          |] `shouldRenderTo` executable_ "foo" [i|
          build-depends:
              base >=2
          |]

    describe "pkg-config-dependencies" $ do
      it "accepts pkg-config-dependencies" $ do
        [i|
        pkg-config-dependencies:
          - QtWebKit
          - weston
        executable: {}
        |] `shouldRenderTo` executable_ "foo" [i|
        pkgconfig-depends:
            QtWebKit
          , weston
        |]

      it "accepts pkgconfig-depends as an alias" $ do
        [i|
        pkgconfig-depends:
          - QtWebKit
          - weston
        executable: {}
        |] `shouldRenderTo` executable_ "foo" [i|
        pkgconfig-depends:
            QtWebKit
          , weston
        |]

    describe "include-dirs" $ do
      it "accepts include-dirs" $ do
        [i|
        include-dirs:
          - foo
          - bar
        executable: {}
        |] `shouldRenderTo` executable_ "foo" [i|
        include-dirs:
            foo
            bar
        |]

    describe "install-includes" $ do
      it "accepts install-includes" $ do
        [i|
        install-includes:
          - foo.h
          - bar.h
        executable: {}
        |] `shouldRenderTo` executable_ "foo" [i|
        install-includes:
            foo.h
            bar.h
        |]

    describe "js-sources" $ before_ (touch "foo.js" >> touch "jsbits/bar.js") $ do
      it "accepts js-sources" $ do
        [i|
        executable:
          js-sources:
            - foo.js
            - jsbits/*.js
        |] `shouldRenderTo` executable_ "foo" [i|
        js-sources:
            foo.js
            jsbits/bar.js
        |]

      it "accepts global js-sources" $ do
        [i|
        js-sources:
          - foo.js
          - jsbits/*.js
        executable: {}
        |] `shouldRenderTo` executable_ "foo" [i|
        js-sources:
            foo.js
            jsbits/bar.js
        |]

    describe "cxx-options" $ do
      it "accepts cxx-options" $ do
        [i|
        executable:
          cxx-options: -Wall
        |] `shouldRenderTo` (executable_ "foo" [i|
        autogen-modules:
            Paths_foo
        cxx-options: -Wall
        |]) {packageCabalVersion = "2.2"}

      context "when used inside a nested conditional" $ do
        it "infers correct cabal-version" $ do
          [i|
          executable:
            when:
              condition: True
              when:
                condition: True
                when:
                  condition: True
                  cxx-options: -Wall
          |] `shouldRenderTo` (executable "foo" [i|
          other-modules:
              Paths_foo
          autogen-modules:
              Paths_foo
          default-language: Haskell2010
          if true
            if true
              if true
                cxx-options: -Wall
          |]) {packageCabalVersion = "2.2"}

    describe "cxx-sources" $ before_ (touch "foo.cc" >> touch "cxxbits/bar.cc") $ do
      it "accepts cxx-sources" $ do
        [i|
        executable:
          cxx-sources:
            - foo.cc
            - cxxbits/*.cc
        |] `shouldRenderTo` (executable_ "foo" [i|
        autogen-modules:
            Paths_foo
        cxx-sources:
            foo.cc
            cxxbits/bar.cc
        |]) {packageCabalVersion = "2.2"}

    describe "language" $ do
      it "accepts language" $ do
        [i|
        language: GHC2021
        executable: {}
        |] `shouldRenderTo` executable "foo" [i|
          other-modules:
              Paths_foo
          default-language: GHC2021
        |]

      it "omits language if it is null" $ do
        [i|
        language: null
        executable: {}
        |] `shouldRenderTo` executable "foo" [i|
          other-modules:
              Paths_foo
        |]

      it "accepts default-language as an alias" $ do
        [i|
        default-language: GHC2021
        executable: {}
        |] `shouldRenderTo` (executable "foo" [i|
          other-modules:
              Paths_foo
          default-language: GHC2021
              Foo
          |]

      context "when inferring modules" $ do
        it "ignores duplicate source directories" $ do
          touch "src/Foo.hs"
          [i|
    describe "frameworks" $ do
      it "accepts frameworks" $ do
        [i|
        frameworks:
          - foo
          - bar
        executable: {}
        |] `shouldRenderTo` executable_ "foo" [i|
        frameworks:
            foo
            bar
        |]

    describe "c-sources" $ before_ (touch "cbits/foo.c" >> touch "cbits/bar.c" >> touch "cbits/baz.c") $ do
      it "keeps declaration order" $ do
        -- IMPORTANT: This is crucial as a workaround for https://ghc.haskell.org/trac/ghc/ticket/13786
        [i|
        library:
          c-sources:
            - cbits/foo.c
            - cbits/bar.c
            - cbits/baz.c
        |] `shouldRenderTo` library_ [i|
        c-sources:
            cbits/foo.c
            cbits/bar.c
            cbits/baz.c
        |]

      it "accepts glob patterns" $ do
        [i|
        library:
          c-sources: cbits/*.c
        |] `shouldRenderTo` library_ [i|
        c-sources:
            cbits/bar.c
            cbits/baz.c
            cbits/foo.c
        |]

      it "warns when a glob pattern does not match any files" $ do
        [i|
        name: foo
        library:
          c-sources: foo/*.c
        |] `shouldWarn` pure "Specified pattern \"foo/*.c\" for c-sources does not match any files"

      it "quotes filenames with special characters" $ do
        touch "cbits/foo bar.c"
        [i|
        library:
          c-sources:
            - cbits/foo bar.c
        |] `shouldRenderTo` library_ [i|
        c-sources:
            "cbits/foo bar.c"
        |]

    describe "custom-setup" $ do
      it "warns on unknown fields" $ do
        [i|
        name: foo
        custom-setup:
          foo: 1
          bar: 2
        |] `shouldWarn` [
            "package.yaml: Ignoring unrecognized field $.custom-setup.bar"
          , "package.yaml: Ignoring unrecognized field $.custom-setup.foo"
          ]

      it "accepts dependencies" $ do
        [i|
        custom-setup:
          dependencies:
            - base
        |] `shouldRenderTo` customSetup [i|
        setup-depends:
            base
        |]

      it "leaves build-type alone, if it exists" $ do
        [i|
        build-type: Make
        custom-setup:
          dependencies:
            - base
        |] `shouldRenderTo` (customSetup [i|
        setup-depends:
            base
        |]) {packageBuildType = "Make"}

    describe "library" $ do
      it "accepts reexported-modules" $ do
        [i|
        library:
          reexported-modules: Baz
        |] `shouldRenderTo` (library_ [i|
          reexported-modules:
              Baz
        |]) {packageCabalVersion = "1.22"}

      it "accepts signatures" $ do
        [i|
        library:
          signatures: Foo
        |] `shouldRenderTo` (library_ [i|
          autogen-modules:
              Paths_foo
          signatures:
              Foo
        |]) {packageCabalVersion = "2.0"}

      context "when package.yaml contains duplicate modules" $ do
        it "generates a cabal file with duplicate modules" $ do
          -- garbage in, garbage out
          [i|
          library:
            exposed-modules: Foo
            other-modules: Foo
          |] `shouldRenderTo` library [i|
          exposed-modules:
              Foo
          other-modules:
              Foo
          default-language: Haskell2010
          |]

      context "with mixins" $ do
        it "infers cabal-version 2.0" $ do
          [i|
          library:
            dependencies:
              foo:
                mixin:
                  - (Blah as Etc)
          |] `shouldRenderTo` (library [i|
          other-modules:
              Paths_foo
          autogen-modules:
              Paths_foo
          build-depends:
              foo
          mixins:
              foo (Blah as Etc)
          default-language: Haskell2010
          |]) {packageCabalVersion = "2.0"}

    describe "internal-libraries" $ do
      it "accepts internal-libraries" $ do
        touch "src/Foo.hs"
        [i|
        internal-libraries:
          bar:
            source-dirs: src
        |] `shouldRenderTo` internalLibrary "bar" [i|
        exposed-modules:
            Foo
        other-modules:
            Paths_foo
        autogen-modules:
            Paths_foo
        hs-source-dirs:
            src
        |]

      it "warns on unknown fields" $ do
        [i|
        name: foo
        internal-libraries:
          bar:
            baz: 42
        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"

      it "warns on missing source-dirs" $ do
        [i|
        name: foo
        internal-libraries:
          bar:
            source-dirs: src
        |] `shouldWarn` pure "Specified source-dir \"src\" does not exist"

      it "accepts visibility" $ do
        [i|
        internal-libraries:
          bar:
            visibility: public
        |] `shouldRenderTo` (internalLibrary "bar" [i|
        visibility: public
        other-modules:
            Paths_foo
        autogen-modules:
            Paths_foo
        |]) {packageCabalVersion = "3.0"}

    context "when inferring modules" $ do
      context "with a library" $ do
        it "ignores duplicate source directories" $ do
          touch "src/Foo.hs"
          [i|
          source-dirs: src
          library:
            source-dirs: src
          |] `shouldRenderTo` library [i|
          hs-source-dirs:
              src
          exposed-modules:
              Foo
          other-modules:
              Paths_foo
          default-language: Haskell2010
          |]

        it "ignores duplicate modules" $ do
          touch "src/Foo.hs"
          touch "src/Foo.x"
          [i|
          library:
            source-dirs: src
          |] `shouldRenderTo` library [i|
          hs-source-dirs:
              src
          exposed-modules:
              Foo
          other-modules:
                  Exposed
            |]) {packageCabalVersion = "2.0"}

      context "with mixins" $ do
        it "infers cabal-version 2.0" $ do
          [i|
          library:
            dependencies:
              foo:
                mixin:
                  - (Blah as Etc)
          |] `shouldRenderTo` (library [i|
          other-modules:
              Paths_foo
          build-depends:
              foo
          mixins:
              foo (Blah as Etc)
          |]) {packageCabalVersion = "2.0"}

    describe "internal-libraries" $ do
      it "accepts internal-libraries" $ do
        touch "src/Foo.hs"
        [i|
        internal-libraries:
          bar:
            source-dirs: src
        |] `shouldRenderTo` internalLibrary "bar" [i|
        exposed-modules:
            Foo
        other-modules:
            Paths_foo
        hs-source-dirs:
            src
        |]

      it "warns on unknown fields" $ do
        [i|
        name: foo
        internal-libraries:
          bar:
            baz: 42
        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"

      it "warns on missing source-dirs" $ do
        [i|
        name: foo
        internal-libraries:
          bar:
            source-dirs: src
        |] `shouldWarn` pure "Specified source-dir \"src\" does not exist"

      it "accepts visibility" $ do
        [i|
        internal-libraries:
          bar:
            visibility: public
        |] `shouldRenderTo` (internalLibrary "bar" [i|
        visibility: public
        other-modules:
            Paths_foo
        |]) {packageCabalVersion = "3.0"}

    describe "executables" $ do
      it "accepts arbitrary entry points as main" $ do
        touch "src/Foo.hs"
        touch "src/Bar.hs"
        [i|
        executables:
          foo:
            source-dirs: src
            main: Foo
        |] `shouldRenderTo` executable "foo" [i|
        main-is: Foo.hs
        ghc-options: -main-is Foo
        hs-source-dirs:
            src
        other-modules:
            Bar
            Paths_foo
        |]

      context "when inferring modules" $ do
        it "infers other-modules" $ do
          touch "src/Main.hs"
          touch "src/Foo.hs"
            touch "src/Bar.hs"
            [i|
            library:
              source-dirs: src
              exposed-modules: Foo
            |] `shouldRenderTo` library [i|
            hs-source-dirs:
                src
            exposed-modules:
                Foo
            other-modules:
                Bar
                Paths_foo
            default-language: Haskell2010
            |]

        context "with other-modules" $ do
          it "infers exposed-modules" $ do
            touch "src/Foo.hs"
            touch "src/Bar.hs"
            [i|
            library:
              source-dirs: src
              other-modules: Bar
            |] `shouldRenderTo` library [i|
            hs-source-dirs:
                src
            exposed-modules:
                Foo
            other-modules:
                Bar
            default-language: Haskell2010
            |]

        context "with both exposed-modules and other-modules" $ do
          it "doesn't infer any modules" $ do
            touch "src/Foo.hs"
            touch "src/Bar.hs"
            [i|
            library:
              source-dirs: src
              exposed-modules: Foo
              other-modules: Bar
            |] `shouldRenderTo` library [i|
            hs-source-dirs:
                src
            exposed-modules:
                Foo
          |]) {packageCabalVersion = "2.0"}

        context "with conditional" $ do
          it "doesn't infer any modules mentioned in that conditional" $ do
            touch "src/Foo.hs"
            touch "src/Bar.hs"
          it "infers exposed-modules" $ do
            touch "src/Foo.hs"
            touch "src/Bar.hs"
            [i|
            library:
              source-dirs: src
            |] `shouldRenderTo` library [i|
            hs-source-dirs:
                src
            exposed-modules:
                Bar
                Foo
            other-modules:
                Paths_foo
            default-language: Haskell2010
            |]

        context "with a conditional" $ do
          it "doesn't infer any modules mentioned in that conditional" $ do
            touch "src/Foo.hs"
            touch "src/Bar.hs"
            [i|
            library:
              source-dirs: src
              when:
                condition: os(windows)
                exposed-modules:
                  - Foo
                  - Paths_foo
            |] `shouldRenderTo` package [i|
            library
              hs-source-dirs:
                  src
              exposed-modules:
                  Bar
              default-language: Haskell2010
              if os(windows)
                exposed-modules:
                  windows
            |]

      context "with conditional" $ do
        it "does not apply global options" $ do
          -- related bug: https://github.com/sol/hpack/issues/214
          [i|
              touch "unix/Foo.hs"
              [i|
              library:
                exposed-modules: Foo
                when:
                  condition: os(windows)
                  then:
                    source-dirs: windows/
                  else:
                    source-dirs: unix/

              |] `shouldRenderTo` package [i|
              library
                exposed-modules:
                    Foo
                other-modules:
                    Paths_foo
                default-language: Haskell2010
                if os(windows)
                  hs-source-dirs:
                      windows/
                else
                  hs-source-dirs:
                      unix/
              |]

        context "with generated modules" $ do
          it "includes generated modules in autogen-modules" $ do
            [i|
            library:
              generated-exposed-modules: Foo
              generated-other-modules: Bar
            |] `shouldRenderTo` (library [i|
            exposed-modules:
                Foo
            other-modules:
                Paths_foo
                Bar
            autogen-modules:
                Paths_foo
                Foo
                Bar
            default-language: Haskell2010
            |]) {packageCabalVersion = "2.0"}

          it "does not infer any mentioned generated modules" $ do
            touch "src/Exposed.hs"
            touch "src/Other.hs"
            [i|
            library:
              source-dirs: src
              generated-exposed-modules: Exposed
              generated-other-modules: Other
            |] `shouldRenderTo` (library [i|
            hs-source-dirs:
                src
            exposed-modules:
                Exposed
            other-modules:
                Paths_foo
                Other
            autogen-modules:
                Paths_foo
                Exposed
                Other
            default-language: Haskell2010
            |]) {packageCabalVersion = "2.0"}

          it "does not infer any generated modules mentioned inside conditionals" $ do
            touch "src/Exposed.hs"
            touch "src/Other.hs"
            [i|
            library:
              source-dirs: src
              when:
                condition: os(windows)
                generated-exposed-modules: Exposed
                generated-other-modules: Other
            |] `shouldRenderTo` (package [i|
            library
              other-modules:
                  Paths_foo
              autogen-modules:
                  Paths_foo
              hs-source-dirs:
                  src
              default-language: Haskell2010
              if os(windows)
                exposed-modules:
                    Exposed
                other-modules:
                    Other
                autogen-modules:
                    Other
                    Exposed
            |]) {packageCabalVersion = "2.0"}

      context "with an executable" $ do
        it "infers other-modules" $ do
          touch "src/Main.hs"
          touch "src/Foo.hs"
          [i|
          executables:
            foo:
              main: Main.hs
              source-dirs: src
          |] `shouldRenderTo` executable "foo" [i|
            main-is: Main.hs
            hs-source-dirs:
                src
            other-modules:
                Foo
                Paths_foo
            default-language: Haskell2010
          |]

        it "allows to specify other-modules" $ do
          touch "src/Foo.hs"
          touch "src/Bar.hs"
          [i|
          executables:
            foo:
              main: Main.hs
              source-dirs: src
              other-modules: Baz
          |] `shouldRenderTo` executable "foo" [i|
            main-is: Main.hs
            hs-source-dirs:
                src
            other-modules:
                Baz
            default-language: Haskell2010
          |]

        it "does not infer any mentioned generated modules" $ do
          touch "src/Foo.hs"
          [i|
          executables:
            foo:
              main: Main.hs
              source-dirs: src
              generated-other-modules: Foo
          |] `shouldRenderTo` (executable "foo" [i|
            main-is: Main.hs
            hs-source-dirs:
                src
            other-modules:
                Paths_foo
                Foo
            autogen-modules:
                Paths_foo
                Foo
            default-language: Haskell2010
          |]) {packageCabalVersion = "2.0"}

        context "with a conditional" $ do
          it "doesn't infer any modules mentioned in that conditional" $ do
            touch "src/Foo.hs"
            touch "src/Bar.hs"
            [i|
            executables:
              foo:
                source-dirs: src
                when:
                  condition: os(windows)
                  other-modules: Foo
            |] `shouldRenderTo` executable "foo" [i|
            other-modules:
                Bar
                Paths_foo
            hs-source-dirs:
                src
            default-language: Haskell2010
            if os(windows)
              other-modules:
                  Foo
            |]

          it "infers other-modules" $ do
            touch "src/Foo.hs"
            touch "windows/Bar.hs"
            [i|
            executables:
              foo:
                source-dirs: src
                when:
                  condition: os(windows)
                  source-dirs: windows
            |] `shouldRenderTo` executable "foo" [i|
            other-modules:
                Foo
                Paths_foo
            hs-source-dirs:
                src
            default-language: Haskell2010
            if os(windows)
              other-modules:
                  Bar
              hs-source-dirs:
                  windows
            |]

    describe "executables" $ do
      it "accepts main-is as an alias for main" $ do
        [i|
        executable:
          main-is: Foo.hs
        |] `shouldRenderTo` (executable_ "foo" [i|
        main-is: Foo.hs
        |]) {
          packageWarnings = ["package.yaml: $.executable.main-is is deprecated, use $.executable.main instead"]
        }

      it "accepts arbitrary entry points as main" $ do
        touch "src/Foo.hs"
        touch "src/Bar.hs"
        [i|
        executables:
          foo:
            source-dirs: src
            main: Foo
        |] `shouldRenderTo` executable "foo" [i|
        main-is: Foo.hs
        ghc-options: -main-is Foo
        hs-source-dirs:
            src
        other-modules:
            Bar
            Paths_foo
        default-language: Haskell2010
        |]

      context "with a conditional" $ do
        it "does not apply global options" $ do
          -- related bug: https://github.com/sol/hpack/issues/214
          [i|
          ghc-options: -Wall
          executables:
            foo:
              when:
                condition: os(windows)
                main: Foo.hs
          |] `shouldRenderTo` executable "foo" [i|
          ghc-options: -Wall
          other-modules:
              Paths_foo
          default-language: Haskell2010
          if os(windows)
            main-is: Foo.hs
          |]

        it "accepts executable-specific fields" $ do
          [i|
          executables:
            foo:
              when:
                condition: os(windows)
                main: Foo
          |] `shouldRenderTo` executable "foo" [i|
          other-modules:
              Paths_foo
          default-language: Haskell2010
          if os(windows)
            main-is: Foo.hs
            ghc-options: -main-is Foo
          |]

    describe "when" $ do
      it "accepts conditionals" $ do
        [i|
        when:
          condition: os(windows)
          dependencies: Win32
        executable: {}
        |] `shouldRenderTo` executable "foo" [i|
        other-modules:
            Paths_foo
        default-language: Haskell2010
        if os(windows)
          build-depends:
              Win32
        |]
      it "warns on unknown fields" $ do
        [i|
        name: foo
        foo: 23
        when:
          - condition: os(windows)
            bar: 23
            when:
              condition: os(windows)
              bar2: 23
          - condition: os(windows)
            baz: 23
        |] `shouldWarn` [
            "package.yaml: Ignoring unrecognized field $.foo"
          , "package.yaml: Ignoring unrecognized field $.when[0].bar"
          , "package.yaml: Ignoring unrecognized field $.when[0].when.bar2"
          , "package.yaml: Ignoring unrecognized field $.when[1].baz"
          ]

      context "when parsing conditionals with else-branch" $ do
        it "accepts conditionals with else-branch" $ do
          [i|
          when:
            condition: os(windows)
            then:
              dependencies: Win32
            else:
              dependencies: unix
          executable: {}
          |] `shouldRenderTo` executable "foo" [i|
          other-modules:
              Paths_foo
          default-language: Haskell2010
          if os(windows)
            build-depends:
                Win32
          else
            build-depends:
                unix
          |]

        context "with empty then-branch" $ do
          it "provides a hint" $ do
            [i|
            when:
              condition: os(windows)
              then: {}
              else:
                dependencies: unix
            executable: {}
            |] `shouldFailWith` unlines [
                "package.yaml: Error while parsing $.when - an empty \"then\" section is not allowed, try the following instead:"
              , ""
              , "when:"
              , "  condition: '!(os(windows))'"
              , "  dependencies: unix"
              ]

        context "with empty else-branch" $ do
          it "provides a hint" $ do
            [i|
            when:
              condition: os(windows)
              then:
                dependencies: Win32
              else: {}
            executable: {}
            |] `shouldFailWith` unlines [
                "package.yaml: Error while parsing $.when - an empty \"else\" section is not allowed, try the following instead:"
              , ""
              , "when:"
              , "  condition: os(windows)"
              , "  dependencies: Win32"
              ]

        it "rejects invalid conditionals" $ do
          [i|
          when:
            condition: os(windows)
            then:
              dependencies: Win32
            else: null
          |] `shouldFailWith` "package.yaml: Error while parsing $.when.else - expected Object, but encountered Null"

        it "rejects invalid conditionals" $ do
          [i|
            dependencies:
              - foo
              - 23
          |] `shouldFailWith` "package.yaml: Error while parsing $.dependencies[1] - expected Object or String, but encountered Number"

        it "warns on unknown fields" $ do
          [i|
          name: foo
          when:
            condition: os(windows)
            foo: null
            then:
              bar: null
            else:
              when:
                condition: os(windows)
                then:
                  dependencies: foo
                else:
                  baz: null
          |] `shouldWarn` [
              "package.yaml: Ignoring unrecognized field $.when.foo"
            , "package.yaml: Ignoring unrecognized field $.when.then.bar"
            , "package.yaml: Ignoring unrecognized field $.when.else.when.else.baz"
            ]

    describe "verbatim" $ do
      it "accepts strings" $ do
        [i|
        library:
          verbatim: |
            foo: 23
            bar: 42
        |] `shouldRenderTo` package [i|
        library
          other-modules:
              Paths_foo
          default-language: Haskell2010
          foo: 23
          bar: 42
        |]

      it "accepts multi-line strings as field values" $ do
        [i|
        library:
          verbatim:
            build-depneds: |
              foo
              bar
              baz
        |] `shouldRenderTo` package [i|
        library
          other-modules:
              Paths_foo
          default-language: Haskell2010
          build-depneds:
              foo
              bar
              baz
        |]

      it "allows to null out existing fields" $ do
        [i|
        library:
          verbatim:
            default-language: null
        |] `shouldRenderTo` package [i|
        library
          other-modules:
              Paths_foo
        |]

      context "when specified globally" $ do
        it "overrides header fields" $ do
          [i|
          verbatim:
            cabal-version: foo
          |] `shouldRenderTo` (package "") {packageCabalVersion = "foo"}

        it "overrides other fields" $ do
          touch "foo"
          [i|
          extra-source-files: foo
          verbatim:
            extra-source-files: bar
          |] `shouldRenderTo` package [i|
          extra-source-files: bar
          |]

        it "is not propagated into sections" $ do
          [i|
          verbatim:
            foo: 23
          library: {}
          |] `shouldRenderTo` package [i|
          foo: 23
          library
            other-modules:
                Paths_foo
            default-language: Haskell2010
          |]

      context "within a section" $ do
        it "overrides section fields" $ do
          [i|
          tests:
            spec:
              verbatim:
                type: detailed-0.9
          |] `shouldRenderTo` package [i|
          test-suite spec
            type: detailed-0.9
            other-modules:
                Paths_foo
            default-language: Haskell2010
          |]
    describe "default value of maintainer" $ do
      it "gives maintainer precedence" $ do
        [i|
          author: John Doe
          maintainer: Jane Doe
          |] `shouldRenderTo` package [i|
          author: John Doe
          maintainer: Jane Doe
          |]
      context "with author" $ do
        it "uses author if maintainer is not specified" $ do
          [i|
            author: John Doe
            |] `shouldRenderTo` package [i|
            author: John Doe
            maintainer: John Doe
            |]
        it "omits maintainer if it is null" $ do
          [i|
            author: John Doe
            maintainer: null
            |] `shouldRenderTo` package [i|
            author: John Doe
            |]

run :: HasCallStack => FilePath -> FilePath -> String -> IO ([String], String)
run userDataDir c old = run_ userDataDir c old >>= either assertFailure return

run_ :: FilePath -> FilePath -> String -> IO (Either String ([String], String))
run_ userDataDir c old = do
  mPackage <- readPackageConfig defaultDecodeOptions {decodeOptionsTarget = c, decodeOptionsUserDataDir = Just userDataDir}
  return $ case mPackage of
    Right (DecodeResult pkg cabalVersion _ warnings) ->
      let
        FormattingHints{..} = sniffFormattingHints (lines old)
        alignment = fromMaybe 0 formattingHintsAlignment
        settings = formattingHintsRenderSettings
        output = cabalVersion ++ Hpack.renderPackageWith settings alignment formattingHintsFieldOrder formattingHintsSectionsFieldOrder pkg
      in
        Right (warnings, output)
    Left err -> Left err

data RenderResult = RenderResult [String] String
  deriving Eq

instance Show RenderResult where
  show (RenderResult warnings output) = unlines (map ("WARNING: " ++) warnings) ++ output

shouldRenderTo :: HasCallStack => String -> Package -> Expectation
shouldRenderTo input p = do
  writeFile packageConfig ("name: foo\n" ++ unindent input)
  (warnings, output) <- run "" packageConfig expected
  RenderResult warnings (dropEmptyLines output) `shouldBe` RenderResult (packageWarnings p) expected
  where
    expected = dropEmptyLines (renderPackage p)
    dropEmptyLines = unlines . filter (not . null) . lines

shouldWarn :: HasCallStack => String -> [String] -> Expectation
shouldWarn input expected = do
  writeFile packageConfig input
  (warnings, _) <- run "" packageConfig ""
  sort warnings `shouldBe` sort expected

shouldFailWith :: HasCallStack => String -> String -> Expectation
shouldFailWith input expected = do
  writeFile packageConfig input
  run_ "" packageConfig "" `shouldReturn` Left expected

customSetup :: String -> Package
customSetup a = (package content) {packageCabalVersion = "1.24", packageBuildType = "Custom"}
  where
    content = [i|
custom-setup
#{indentBy 2 $ unindent a}
|]

library_ :: String -> Package
library_ l = package content
  where
    content = [i|
library
  other-modules:
      Paths_foo
#{indentBy 2 $ unindent l}
  default-language: Haskell2010
|]

library :: String -> Package
library l = package content
  where
    content = [i|
library
#{indentBy 2 $ unindent l}
|]

internalLibrary :: String -> String -> Package
internalLibrary name e = (package content) {packageCabalVersion = "2.0"}
  where
    content = [i|
library #{name}
#{indentBy 2 $ unindent e}
  default-language: Haskell2010
|]

executable_ :: String -> String -> Package
executable_ name e = package content
  where
    content = [i|
executable #{name}
  other-modules:
      Paths_foo
#{indentBy 2 $ unindent e}
  default-language: Haskell2010
|]

executable :: String -> String -> Package
executable name e = package content
  where
    content = [i|
executable #{name}
#{indentBy 2 $ unindent e}
|]

package :: String -> Package
package c = Package "foo" "0.0.0" "Simple" "1.12" c []

data Package = Package {
  packageName :: String
, packageVersion :: String
, packageBuildType :: String
, packageCabalVersion :: String
, packageContent :: String
, packageWarnings :: [String]
}

renderPackage :: Package -> String
renderPackage Package{..} = unindent [i|
cabal-version: #{packageCabalVersion}
name: #{packageName}
version: #{packageVersion}
build-type: #{packageBuildType}

#{unindent packageContent}
|]

indentBy :: Int -> String -> String
indentBy n = unlines . map (replicate n ' ' ++) . lines

license :: String
license = [i|
Copyright (c) 2014-2018 Simon Hengel <sol@typeful.net>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
|]

<MSG> Minor refactoring

<DFF> @@ -986,7 +986,68 @@ spec = around_ (inTempDirectoryNamed "foo") $ do
               Foo
           |]
 
-      context "when inferring modules" $ do
+      context "with mixins" $ do
+        it "infers cabal-version 2.0" $ do
+          [i|
+          library:
+            dependencies:
+              foo:
+                mixin:
+                  - (Blah as Etc)
+          |] `shouldRenderTo` (library [i|
+          other-modules:
+              Paths_foo
+          build-depends:
+              foo
+          mixins:
+              foo (Blah as Etc)
+          |]) {packageCabalVersion = "2.0"}
+
+    describe "internal-libraries" $ do
+      it "accepts internal-libraries" $ do
+        touch "src/Foo.hs"
+        [i|
+        internal-libraries:
+          bar:
+            source-dirs: src
+        |] `shouldRenderTo` internalLibrary "bar" [i|
+        exposed-modules:
+            Foo
+        other-modules:
+            Paths_foo
+        hs-source-dirs:
+            src
+        |]
+
+      it "warns on unknown fields" $ do
+        [i|
+        name: foo
+        internal-libraries:
+          bar:
+            baz: 42
+        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"
+
+      it "warns on missing source-dirs" $ do
+        [i|
+        name: foo
+        internal-libraries:
+          bar:
+            source-dirs: src
+        |] `shouldWarn` pure "Specified source-dir \"src\" does not exist"
+
+      it "accepts visibility" $ do
+        [i|
+        internal-libraries:
+          bar:
+            visibility: public
+        |] `shouldRenderTo` (internalLibrary "bar" [i|
+        visibility: public
+        other-modules:
+            Paths_foo
+        |]) {packageCabalVersion = "3.0"}
+
+    context "when inferring modules" $ do
+      context "with a library" $ do
         it "ignores duplicate source directories" $ do
           touch "src/Foo.hs"
           [i|
@@ -1217,86 +1278,7 @@ spec = around_ (inTempDirectoryNamed "foo") $ do
                   Exposed
             |]) {packageCabalVersion = "2.0"}
 
-      context "with mixins" $ do
-        it "infers cabal-version 2.0" $ do
-          [i|
-          library:
-            dependencies:
-              foo:
-                mixin:
-                  - (Blah as Etc)
-          |] `shouldRenderTo` (library [i|
-          other-modules:
-              Paths_foo
-          build-depends:
-              foo
-          mixins:
-              foo (Blah as Etc)
-          |]) {packageCabalVersion = "2.0"}
-
-    describe "internal-libraries" $ do
-      it "accepts internal-libraries" $ do
-        touch "src/Foo.hs"
-        [i|
-        internal-libraries:
-          bar:
-            source-dirs: src
-        |] `shouldRenderTo` internalLibrary "bar" [i|
-        exposed-modules:
-            Foo
-        other-modules:
-            Paths_foo
-        hs-source-dirs:
-            src
-        |]
-
-      it "warns on unknown fields" $ do
-        [i|
-        name: foo
-        internal-libraries:
-          bar:
-            baz: 42
-        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"
-
-      it "warns on missing source-dirs" $ do
-        [i|
-        name: foo
-        internal-libraries:
-          bar:
-            source-dirs: src
-        |] `shouldWarn` pure "Specified source-dir \"src\" does not exist"
-
-      it "accepts visibility" $ do
-        [i|
-        internal-libraries:
-          bar:
-            visibility: public
-        |] `shouldRenderTo` (internalLibrary "bar" [i|
-        visibility: public
-        other-modules:
-            Paths_foo
-        |]) {packageCabalVersion = "3.0"}
-
-    describe "executables" $ do
-      it "accepts arbitrary entry points as main" $ do
-        touch "src/Foo.hs"
-        touch "src/Bar.hs"
-        [i|
-        executables:
-          foo:
-            source-dirs: src
-            main: Foo
-        |] `shouldRenderTo` executable "foo" [i|
-        main-is: Foo.hs
-        ghc-options: -main-is Foo
-        hs-source-dirs:
-            src
-        other-modules:
-            Bar
-            Paths_foo
-        |]
-
-      context "when inferring modules" $ do
+      context "with an executable" $ do
         it "infers other-modules" $ do
           touch "src/Main.hs"
           touch "src/Foo.hs"
@@ -1350,7 +1332,7 @@ spec = around_ (inTempDirectoryNamed "foo") $ do
                 Foo
           |]) {packageCabalVersion = "2.0"}
 
-        context "with conditional" $ do
+        context "with a conditional" $ do
           it "doesn't infer any modules mentioned in that conditional" $ do
             touch "src/Foo.hs"
             touch "src/Bar.hs"
@@ -1395,7 +1377,26 @@ spec = around_ (inTempDirectoryNamed "foo") $ do
                   windows
             |]
 
-      context "with conditional" $ do
+    describe "executables" $ do
+      it "accepts arbitrary entry points as main" $ do
+        touch "src/Foo.hs"
+        touch "src/Bar.hs"
+        [i|
+        executables:
+          foo:
+            source-dirs: src
+            main: Foo
+        |] `shouldRenderTo` executable "foo" [i|
+        main-is: Foo.hs
+        ghc-options: -main-is Foo
+        hs-source-dirs:
+            src
+        other-modules:
+            Bar
+            Paths_foo
+        |]
+
+      context "with a conditional" $ do
         it "does not apply global options" $ do
           -- related bug: https://github.com/sol/hpack/issues/214
           [i|

{'commit': 'diff --git a/src/Hpack/Config.hs b/src/Hpack/Config.hs\nindex ec72437..63deee7 100644\n--- a/src/Hpack/Config.hs\n+++ b/src/Hpack/Config.hs\n@@ -837,8 +837,6 @@ determineCabalVersion inferredLicense pkg@Package{..} = (\n         p sect\n       , any (any (sectionSatisfies p)) (sectionConditionals sect)\n       ]\n-    sectionAll :: (Semigroup b, Monoid b) => (Section a -> b) -> Section a -> b\n-    sectionAll f sect = f sect <> foldMap (foldMap $ sectionAll f) (sectionConditionals sect)\n \n     hasMixins :: DependencyInfo -> Bool\n     hasMixins (DependencyInfo mixins _) = not (null mixins)\n@@ -846,6 +844,9 @@ determineCabalVersion inferredLicense pkg@Package{..} = (\n     hasSubcomponents :: String -> Bool\n     hasSubcomponents = elem \':\'\n \n+sectionAll :: (Semigroup b, Monoid b) => (Section a -> b) -> Section a -> b\n+sectionAll f sect = f sect <> foldMap (foldMap $ sectionAll f) (sectionConditionals sect)\n+\n decodeValue :: FromValue a => ProgramName -> FilePath -> Value -> Warnings (Errors IO) a\n decodeValue (ProgramName programName) file value = do\n   (r, unknown) <- lift . ExceptT . return $ first (prefix ++) (Config.decodeValue value)\n@@ -1107,11 +1108,14 @@ toPackage_ dir (Product g PackageConfig{..}) = do\n     toSect :: (Monad m, Monoid a) => WithCommonOptions CSources CxxSources JsSources a -> Warnings m (Section a)\n     toSect = toSection packageName_ executableNames . first ((mempty <$ globalOptions) <>)\n \n-    toLib = toSect >=> liftIO . toLibrary dir packageName_\n-    toExecutables = maybe (return mempty) (traverse $ toSect >=> liftIO . toExecutable dir packageName_)\n+    toSections :: (Monad m, Monoid a) => Maybe (Map String (WithCommonOptions CSources CxxSources JsSources a)) -> Warnings m (Map String (Section a))\n+    toSections = maybe (return mempty) (traverse toSect)\n+\n+    toLib = liftIO . toLibrary dir packageName_\n+    toExecutables = toSections >=> traverse (liftIO . toExecutable dir packageName_)\n \n-  mLibrary <- traverse toLib packageConfigLibrary\n-  internalLibraries <- maybe (return mempty) (traverse toLib) packageConfigInternalLibraries\n+  mLibrary <- traverse (toSect >=> toLib) packageConfigLibrary\n+  internalLibraries <- toSections packageConfigInternalLibraries >>= traverse toLib\n \n   executables <- toExecutables executableMap\n   tests <- toExecutables packageConfigTests\ndiff --git a/src/Hpack/Module.hs b/src/Hpack/Module.hs\nindex 61c2a2c..cc84991 100644\n--- a/src/Hpack/Module.hs\n+++ b/src/Hpack/Module.hs\n@@ -41,18 +41,26 @@ toModule path = case reverse $ Path.components path of\n   file : dirs -> Module . intercalate "." . reverse $ dropExtension file : dirs\n \n getModules :: FilePath -> FilePath -> IO [Module]\n-getModules dir src_ = sortModules <$> do\n-  exists <- Directory.doesDirectoryExist (dir </> src_)\n+getModules dir literalSrc = sortModules <$> do\n+  exists <- Directory.doesDirectoryExist (dir </> literalSrc)\n   if exists\n     then do\n-      src <- Directory.canonicalizePath (dir </> src_)\n-      removeSetup src . nub . map toModule <$> getModuleFilesRecursive src\n+      canonicalSrc <- Directory.canonicalizePath (dir </> literalSrc)\n+\n+      let\n+        srcIsProjectRoot :: Bool\n+        srcIsProjectRoot = canonicalSrc == dir\n+\n+        toModules :: [Path] -> [Module]\n+        toModules = removeSetup . nub . map toModule\n+\n+        removeSetup :: [Module] -> [Module]\n+        removeSetup\n+          | srcIsProjectRoot = filter (/= "Setup")\n+          | otherwise = id\n+\n+      toModules <$> getModuleFilesRecursive canonicalSrc\n     else return []\n-  where\n-    removeSetup :: FilePath -> [Module] -> [Module]\n-    removeSetup src\n-      | src == dir = filter (/= "Setup")\n-      | otherwise = id\n \n sortModules :: [Module] -> [Module]\n sortModules = map Module . sort . map unModule\ndiff --git a/test/EndToEndSpec.hs b/test/EndToEndSpec.hs\nindex c00ad57..227de42 100644\n--- a/test/EndToEndSpec.hs\n+++ b/test/EndToEndSpec.hs\n@@ -986,7 +986,68 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n               Foo\n           |]\n \n-      context "when inferring modules" $ do\n+      context "with mixins" $ do\n+        it "infers cabal-version 2.0" $ do\n+          [i|\n+          library:\n+            dependencies:\n+              foo:\n+                mixin:\n+                  - (Blah as Etc)\n+          |] `shouldRenderTo` (library [i|\n+          other-modules:\n+              Paths_foo\n+          build-depends:\n+              foo\n+          mixins:\n+              foo (Blah as Etc)\n+          |]) {packageCabalVersion = "2.0"}\n+\n+    describe "internal-libraries" $ do\n+      it "accepts internal-libraries" $ do\n+        touch "src/Foo.hs"\n+        [i|\n+        internal-libraries:\n+          bar:\n+            source-dirs: src\n+        |] `shouldRenderTo` internalLibrary "bar" [i|\n+        exposed-modules:\n+            Foo\n+        other-modules:\n+            Paths_foo\n+        hs-source-dirs:\n+            src\n+        |]\n+\n+      it "warns on unknown fields" $ do\n+        [i|\n+        name: foo\n+        internal-libraries:\n+          bar:\n+            baz: 42\n+        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"\n+\n+      it "warns on missing source-dirs" $ do\n+        [i|\n+        name: foo\n+        internal-libraries:\n+          bar:\n+            source-dirs: src\n+        |] `shouldWarn` pure "Specified source-dir \\"src\\" does not exist"\n+\n+      it "accepts visibility" $ do\n+        [i|\n+        internal-libraries:\n+          bar:\n+            visibility: public\n+        |] `shouldRenderTo` (internalLibrary "bar" [i|\n+        visibility: public\n+        other-modules:\n+            Paths_foo\n+        |]) {packageCabalVersion = "3.0"}\n+\n+    context "when inferring modules" $ do\n+      context "with a library" $ do\n         it "ignores duplicate source directories" $ do\n           touch "src/Foo.hs"\n           [i|\n@@ -1217,86 +1278,7 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n                   Exposed\n             |]) {packageCabalVersion = "2.0"}\n \n-      context "with mixins" $ do\n-        it "infers cabal-version 2.0" $ do\n-          [i|\n-          library:\n-            dependencies:\n-              foo:\n-                mixin:\n-                  - (Blah as Etc)\n-          |] `shouldRenderTo` (library [i|\n-          other-modules:\n-              Paths_foo\n-          build-depends:\n-              foo\n-          mixins:\n-              foo (Blah as Etc)\n-          |]) {packageCabalVersion = "2.0"}\n-\n-    describe "internal-libraries" $ do\n-      it "accepts internal-libraries" $ do\n-        touch "src/Foo.hs"\n-        [i|\n-        internal-libraries:\n-          bar:\n-            source-dirs: src\n-        |] `shouldRenderTo` internalLibrary "bar" [i|\n-        exposed-modules:\n-            Foo\n-        other-modules:\n-            Paths_foo\n-        hs-source-dirs:\n-            src\n-        |]\n-\n-      it "warns on unknown fields" $ do\n-        [i|\n-        name: foo\n-        internal-libraries:\n-          bar:\n-            baz: 42\n-        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"\n-\n-      it "warns on missing source-dirs" $ do\n-        [i|\n-        name: foo\n-        internal-libraries:\n-          bar:\n-            source-dirs: src\n-        |] `shouldWarn` pure "Specified source-dir \\"src\\" does not exist"\n-\n-      it "accepts visibility" $ do\n-        [i|\n-        internal-libraries:\n-          bar:\n-            visibility: public\n-        |] `shouldRenderTo` (internalLibrary "bar" [i|\n-        visibility: public\n-        other-modules:\n-            Paths_foo\n-        |]) {packageCabalVersion = "3.0"}\n-\n-    describe "executables" $ do\n-      it "accepts arbitrary entry points as main" $ do\n-        touch "src/Foo.hs"\n-        touch "src/Bar.hs"\n-        [i|\n-        executables:\n-          foo:\n-            source-dirs: src\n-            main: Foo\n-        |] `shouldRenderTo` executable "foo" [i|\n-        main-is: Foo.hs\n-        ghc-options: -main-is Foo\n-        hs-source-dirs:\n-            src\n-        other-modules:\n-            Bar\n-            Paths_foo\n-        |]\n-\n-      context "when inferring modules" $ do\n+      context "with an executable" $ do\n         it "infers other-modules" $ do\n           touch "src/Main.hs"\n           touch "src/Foo.hs"\n@@ -1350,7 +1332,7 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n                 Foo\n           |]) {packageCabalVersion = "2.0"}\n \n-        context "with conditional" $ do\n+        context "with a conditional" $ do\n           it "doesn\'t infer any modules mentioned in that conditional" $ do\n             touch "src/Foo.hs"\n             touch "src/Bar.hs"\n@@ -1395,7 +1377,26 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n                   windows\n             |]\n \n-      context "with conditional" $ do\n+    describe "executables" $ do\n+      it "accepts arbitrary entry points as main" $ do\n+        touch "src/Foo.hs"\n+        touch "src/Bar.hs"\n+        [i|\n+        executables:\n+          foo:\n+            source-dirs: src\n+            main: Foo\n+        |] `shouldRenderTo` executable "foo" [i|\n+        main-is: Foo.hs\n+        ghc-options: -main-is Foo\n+        hs-source-dirs:\n+            src\n+        other-modules:\n+            Bar\n+            Paths_foo\n+        |]\n+\n+      context "with a conditional" $ do\n         it "does not apply global options" $ do\n           -- related bug: https://github.com/sol/hpack/issues/214\n           [i|\n', 'commit_subject': 'Minor refactoring', 'repo_name': 'sol/hpack', 'committer': {'name': 'Simon Hengel', 'email': 'fbb93bb966c801b3a72230e8f3b752b62ef22929@typeful.net', 'time_sec': '1605207333', 'tz_offset': '420', 'date': {'seconds': '1605207333'}}, 'commit_tree': '1a48dc44c92cdb5d8d978284246717b6d868162a', 'commit_message': 'Minor refactoring\n', 'language_name': 'Shell', 'license': 'mit', 'hunks': '@@ -986,7 +986,68 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n               Foo\n           |]\n \n-      context "when inferring modules" $ do\n+      context "with mixins" $ do\n+        it "infers cabal-version 2.0" $ do\n+          [i|\n+          library:\n+            dependencies:\n+              foo:\n+                mixin:\n+                  - (Blah as Etc)\n+          |] `shouldRenderTo` (library [i|\n+          other-modules:\n+              Paths_foo\n+          build-depends:\n+              foo\n+          mixins:\n+              foo (Blah as Etc)\n+          |]) {packageCabalVersion = "2.0"}\n+\n+    describe "internal-libraries" $ do\n+      it "accepts internal-libraries" $ do\n+        touch "src/Foo.hs"\n+        [i|\n+        internal-libraries:\n+          bar:\n+            source-dirs: src\n+        |] `shouldRenderTo` internalLibrary "bar" [i|\n+        exposed-modules:\n+            Foo\n+        other-modules:\n+            Paths_foo\n+        hs-source-dirs:\n+            src\n+        |]\n+\n+      it "warns on unknown fields" $ do\n+        [i|\n+        name: foo\n+        internal-libraries:\n+          bar:\n+            baz: 42\n+        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"\n+\n+      it "warns on missing source-dirs" $ do\n+        [i|\n+        name: foo\n+        internal-libraries:\n+          bar:\n+            source-dirs: src\n+        |] `shouldWarn` pure "Specified source-dir \\"src\\" does not exist"\n+\n+      it "accepts visibility" $ do\n+        [i|\n+        internal-libraries:\n+          bar:\n+            visibility: public\n+        |] `shouldRenderTo` (internalLibrary "bar" [i|\n+        visibility: public\n+        other-modules:\n+            Paths_foo\n+        |]) {packageCabalVersion = "3.0"}\n+\n+    context "when inferring modules" $ do\n+      context "with a library" $ do\n         it "ignores duplicate source directories" $ do\n           touch "src/Foo.hs"\n           [i|\n@@ -1217,86 +1278,7 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n                   Exposed\n             |]) {packageCabalVersion = "2.0"}\n \n-      context "with mixins" $ do\n-        it "infers cabal-version 2.0" $ do\n-          [i|\n-          library:\n-            dependencies:\n-              foo:\n-                mixin:\n-                  - (Blah as Etc)\n-          |] `shouldRenderTo` (library [i|\n-          other-modules:\n-              Paths_foo\n-          build-depends:\n-              foo\n-          mixins:\n-              foo (Blah as Etc)\n-          |]) {packageCabalVersion = "2.0"}\n-\n-    describe "internal-libraries" $ do\n-      it "accepts internal-libraries" $ do\n-        touch "src/Foo.hs"\n-        [i|\n-        internal-libraries:\n-          bar:\n-            source-dirs: src\n-        |] `shouldRenderTo` internalLibrary "bar" [i|\n-        exposed-modules:\n-            Foo\n-        other-modules:\n-            Paths_foo\n-        hs-source-dirs:\n-            src\n-        |]\n-\n-      it "warns on unknown fields" $ do\n-        [i|\n-        name: foo\n-        internal-libraries:\n-          bar:\n-            baz: 42\n-        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"\n-\n-      it "warns on missing source-dirs" $ do\n-        [i|\n-        name: foo\n-        internal-libraries:\n-          bar:\n-            source-dirs: src\n-        |] `shouldWarn` pure "Specified source-dir \\"src\\" does not exist"\n-\n-      it "accepts visibility" $ do\n-        [i|\n-        internal-libraries:\n-          bar:\n-            visibility: public\n-        |] `shouldRenderTo` (internalLibrary "bar" [i|\n-        visibility: public\n-        other-modules:\n-            Paths_foo\n-        |]) {packageCabalVersion = "3.0"}\n-\n-    describe "executables" $ do\n-      it "accepts arbitrary entry points as main" $ do\n-        touch "src/Foo.hs"\n-        touch "src/Bar.hs"\n-        [i|\n-        executables:\n-          foo:\n-            source-dirs: src\n-            main: Foo\n-        |] `shouldRenderTo` executable "foo" [i|\n-        main-is: Foo.hs\n-        ghc-options: -main-is Foo\n-        hs-source-dirs:\n-            src\n-        other-modules:\n-            Bar\n-            Paths_foo\n-        |]\n-\n-      context "when inferring modules" $ do\n+      context "with an executable" $ do\n         it "infers other-modules" $ do\n           touch "src/Main.hs"\n           touch "src/Foo.hs"\n@@ -1350,7 +1332,7 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n                 Foo\n           |]) {packageCabalVersion = "2.0"}\n \n-        context "with conditional" $ do\n+        context "with a conditional" $ do\n           it "doesn\'t infer any modules mentioned in that conditional" $ do\n             touch "src/Foo.hs"\n             touch "src/Bar.hs"\n@@ -1395,7 +1377,26 @@ spec = around_ (inTempDirectoryNamed "foo") $ do\n                   windows\n             |]\n \n-      context "with conditional" $ do\n+    describe "executables" $ do\n+      it "accepts arbitrary entry points as main" $ do\n+        touch "src/Foo.hs"\n+        touch "src/Bar.hs"\n+        [i|\n+        executables:\n+          foo:\n+            source-dirs: src\n+            main: Foo\n+        |] `shouldRenderTo` executable "foo" [i|\n+        main-is: Foo.hs\n+        ghc-options: -main-is Foo\n+        hs-source-dirs:\n+            src\n+        other-modules:\n+            Bar\n+            Paths_foo\n+        |]\n+\n+      context "with a conditional" $ do\n         it "does not apply global options" $ do\n           -- related bug: https://github.com/sol/hpack/issues/214\n           [i|\n', 'addition_count': 84, 'deletion_count': 83, 'src_file': 'a/test/EndToEndSpec.hs', 'tgt_file': 'b/test/EndToEndSpec.hs', 'file_extension': '.hs', 'before_file': '{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE QuasiQuotes #-}\n{-# LANGUAGE RecordWildCards #-}\n{-# LANGUAGE LambdaCase #-}\n{-# LANGUAGE ConstraintKinds #-}\nmodule EndToEndSpec (spec) where\n\nimport           Prelude hiding (writeFile)\nimport qualified Prelude\n\nimport           Helper\nimport           Test.HUnit\n\nimport           System.Directory (canonicalizePath)\nimport           Data.Maybe\nimport           Data.List\nimport           Data.String.Interpolate\nimport           Data.String.Interpolate.Util\nimport           Data.Version (showVersion)\n\nimport qualified Hpack.Render as Hpack\nimport           Hpack.Config (packageConfig, readPackageConfig, DecodeOptions(..), DecodeResult(..), defaultDecodeOptions)\nimport           Hpack.Render.Hints (FormattingHints(..), sniffFormattingHints)\n\nimport qualified Paths_hpack as Hpack (version)\n\nwriteFile :: FilePath -> String -> IO ()\nwriteFile file c = touch file >> Prelude.writeFile file c\n\nspec :: Spec\nspec = around_ (inTempDirectoryNamed "foo") $ do\n  describe "hpack" $ do\n    it "ignores fields that start with an underscore" $ do\n      [i|\n      _foo:\n        bar: 23\n      library: {}\n      |] `shouldRenderTo` library_ [i|\n      |]\n    it "warns on duplicate fields" $ do\n      [i|\n      name: foo\n      name: foo\n      |] `shouldWarn` [\n          "package.yaml: Duplicate field $.name"\n        ]\n\n    describe "tested-with" $ do\n      it "accepts a string" $ do\n        [i|\n        tested-with: GHC == 7.0.4\n        |] `shouldRenderTo` package [i|\n        tested-with:\n            GHC == 7.0.4\n        |]\n\n      it "accepts a list" $ do\n        [i|\n        tested-with:\n        - GHC == 7.0.4\n        - GHC == 7.2.2\n        - GHC == 7.4.2\n        |] `shouldRenderTo` package [i|\n        tested-with:\n            GHC == 7.0.4\n          , GHC == 7.2.2\n          , GHC == 7.4.2\n        |]\n\n    describe "handling of Paths_ module" $ do\n      it "adds Paths_ to other-modules" $ do\n        [i|\n        library: {}\n        |] `shouldRenderTo` library [i|\n        other-modules:\n            Paths_foo\n        default-language: Haskell2010\n        |]\n\n      context "when cabal-version is >= 2" $ do\n        it "adds Paths_ to autogen-modules" $ do\n          [i|\n          verbatim:\n            cabal-version: 2.0\n          library: {}\n          |] `shouldRenderTo` (library [i|\n          other-modules:\n              Paths_foo\n          autogen-modules:\n              Paths_foo\n          default-language: Haskell2010\n          |]) { packageCabalVersion = "2.0" }\n\n        context "when Paths_ module is listed explicitly under generated-other-modules" $ do\n          it "adds Paths_ to autogen-modules only once" $ do\n            [i|\n            verbatim:\n              cabal-version: 2.0\n            library:\n              generated-other-modules: Paths_foo\n            |] `shouldRenderTo` (library [i|\n            other-modules:\n                Paths_foo\n            autogen-modules:\n                Paths_foo\n            default-language: Haskell2010\n            |]) { packageCabalVersion = "2.0" }\n\n        context "when Paths_ module is listed explicitly under generated-exposed-modules" $ do\n          it "adds Paths_ to autogen-modules only once" $ do\n            [i|\n            verbatim:\n              cabal-version: 2.0\n            library:\n              generated-exposed-modules: Paths_foo\n            |] `shouldRenderTo` (library [i|\n            exposed-modules:\n                Paths_foo\n            autogen-modules:\n                Paths_foo\n            default-language: Haskell2010\n            |]) { packageCabalVersion = "2.0" }\n\n      context "when Paths_ is mentioned in a conditional that is always false" $ do\n        it "does not add Paths_" $ do\n          [i|\n          library:\n            when:\n            - condition: false\n              other-modules: Paths_foo\n          |] `shouldRenderTo` library [i|\n          default-language: Haskell2010\n          |]\n\n      context "when Paths_ is used with RebindableSyntax and (OverloadedStrings or OverloadedLists)" $ do\n        it "infers cabal-version 2.2" $ do\n          [i|\n          default-extensions: [RebindableSyntax, OverloadedStrings]\n          library: {}\n          |] `shouldRenderTo` (library [i|\n          default-extensions:\n              RebindableSyntax\n              OverloadedStrings\n          other-modules:\n              Paths_foo\n          autogen-modules:\n              Paths_foo\n          default-language: Haskell2010\n          |]) {packageCabalVersion = "2.2"}\n\n        context "when Paths_ is mentioned in a conditional that is always false" $ do\n          it "does not infer cabal-version 2.2" $ do\n            [i|\n            default-extensions: [RebindableSyntax, OverloadedStrings]\n            library:\n              when:\n              - condition: false\n                other-modules: Paths_foo\n            |] `shouldRenderTo` (library [i|\n            default-extensions:\n                RebindableSyntax\n                OverloadedStrings\n            default-language: Haskell2010\n            |])\n\n    describe "spec-version" $ do\n      it "accepts spec-version" $ do\n        [i|\n        spec-version: 0.29.5\n        |] `shouldRenderTo` package [i|\n        |]\n\n      it "fails on malformed spec-version" $ do\n        [i|\n        spec-version: foo\n        |] `shouldFailWith` "package.yaml: Error while parsing $.spec-version - invalid value \\"foo\\""\n\n      it "fails on unsupported spec-version" $ do\n        [i|\n        spec-version: 25.0\n        |] `shouldFailWith` ("The file package.yaml requires version 25.0 of the Hpack package specification, however this version of hpack only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of hpack may resolve this issue.")\n\n      it "fails on unsupported spec-version from defaults" $ do\n        let file = joinPath ["defaults", "sol", "hpack-template", "2017", "defaults.yaml"]\n        writeFile file [i|\n        spec-version: 25.0\n        |]\n\n        [i|\n        defaults:\n          github: sol/hpack-template\n          path: defaults.yaml\n          ref: "2017"\n        library: {}\n        |] `shouldFailWith` ("The file " ++ file ++ " requires version 25.0 of the Hpack package specification, however this version of hpack only supports versions up to " ++ showVersion Hpack.version ++ ". Upgrading to the latest version of hpack may resolve this issue.")\n\n    describe "data-files" $ do\n      it "accepts data-files" $ do\n        touch "data/foo/index.html"\n        touch "data/bar/index.html"\n        [i|\n        data-files:\n          - data/**/*.html\n        |] `shouldRenderTo` package [i|\n        data-files:\n            data/bar/index.html\n            data/foo/index.html\n        |]\n\n    describe "data-dir" $ do\n      it "accepts data-dir" $ do\n        touch "data/foo.html"\n        touch "data/bar.html"\n        [i|\n        data-dir: data\n        data-files:\n          - "*.html"\n        |] `shouldRenderTo` package [i|\n        data-files:\n            bar.html\n            foo.html\n        data-dir: data\n        |]\n\n    describe "github" $ do\n      it "accepts owner/repo" $ do\n        [i|\n        github: sol/hpack\n        |] `shouldRenderTo` package [i|\n        homepage: https://github.com/sol/hpack#readme\n        bug-reports: https://github.com/sol/hpack/issues\n        source-repository head\n          type: git\n          location: https://github.com/sol/hpack\n        |]\n\n      it "accepts owner/repo/path" $ do\n        [i|\n        github: hspec/hspec/hspec-core\n        |] `shouldRenderTo` package [i|\n        homepage: https://github.com/hspec/hspec#readme\n        bug-reports: https://github.com/hspec/hspec/issues\n        source-repository head\n          type: git\n          location: https://github.com/hspec/hspec\n          subdir: hspec-core\n        |]\n\n      it "rejects URLs" $ do\n        [i|\n        github: https://github.com/sol/hpack/issues/365\n        |] `shouldFailWith` "package.yaml: Error while parsing $.github - expected owner/repo or owner/repo/subdir, but encountered \\"https://github.com/sol/hpack/issues/365\\""\n\n    describe "homepage" $ do\n      it "accepts homepage URL" $ do\n        [i|\n        homepage: https://example.com/\n        |] `shouldRenderTo` package [i|\n        homepage: https://example.com/\n        |]\n\n      context "with github" $ do\n        it "gives homepage URL precedence" $ do\n          [i|\n          github: hspec/hspec\n          homepage: https://example.com/\n          |] `shouldRenderTo` package [i|\n          homepage: https://example.com/\n          bug-reports: https://github.com/hspec/hspec/issues\n          source-repository head\n            type: git\n            location: https://github.com/hspec/hspec\n          |]\n\n        it "omits homepage URL if it is null" $ do\n          [i|\n          github: hspec/hspec\n          homepage: null\n          |] `shouldRenderTo` package [i|\n          bug-reports: https://github.com/hspec/hspec/issues\n          source-repository head\n            type: git\n            location: https://github.com/hspec/hspec\n          |]\n\n    describe "bug-reports" $ do\n      it "accepts bug-reports URL" $ do\n        [i|\n        bug-reports: https://example.com/\n        |] `shouldRenderTo` package [i|\n        bug-reports: https://example.com/\n        |]\n\n      context "with github" $ do\n        it "gives bug-reports URL precedence" $ do\n          [i|\n          github: hspec/hspec\n          bug-reports: https://example.com/\n          |] `shouldRenderTo` package [i|\n          homepage: https://github.com/hspec/hspec#readme\n          bug-reports: https://example.com/\n          source-repository head\n            type: git\n            location: https://github.com/hspec/hspec\n          |]\n\n        it "omits bug-reports URL if it is null" $ do\n          [i|\n          github: hspec/hspec\n          bug-reports: null\n          |] `shouldRenderTo` package [i|\n          homepage: https://github.com/hspec/hspec#readme\n          source-repository head\n            type: git\n            location: https://github.com/hspec/hspec\n          |]\n\n    describe "defaults" $ do\n      it "accepts global defaults" $ do\n        writeFile "defaults/sol/hpack-template/2017/defaults.yaml" [i|\n        default-extensions:\n          - RecordWildCards\n          - DeriveFunctor\n        |]\n\n        [i|\n        defaults:\n          github: sol/hpack-template\n          path: defaults.yaml\n          ref: "2017"\n        library: {}\n        |] `shouldRenderTo` library_ [i|\n        default-extensions:\n            RecordWildCards\n            DeriveFunctor\n        |]\n\n      it "accepts library defaults" $ do\n        writeFile "defaults/sol/hpack-template/2017/defaults.yaml" [i|\n        exposed-modules: Foo\n        |]\n\n        [i|\n        library:\n          defaults:\n            github: sol/hpack-template\n            path: defaults.yaml\n            ref: "2017"\n        |] `shouldRenderTo` library_ [i|\n        exposed-modules:\n            Foo\n        |]\n\n      it "accepts executable defaults" $ do\n        writeFile "defaults/sol/hpack-template/2017/.hpack/defaults.yaml" [i|\n        main: Foo.hs\n        |]\n\n        [i|\n        executable:\n          defaults: sol/hpack-template@2017\n        |] `shouldRenderTo` executable_ "foo" [i|\n        main-is: Foo.hs\n        |]\n\n      it "gives `main` from executable section precedence" $ do\n        writeFile "defaults/sol/hpack-template/2017/.hpack/defaults.yaml" [i|\n        main: Foo.hs\n        |]\n\n        [i|\n        executable:\n          main: Bar.hs\n          defaults: sol/hpack-template@2017\n        |] `shouldRenderTo` executable_ "foo" [i|\n        main-is: Bar.hs\n        |]\n\n      it "accepts a list of defaults" $ do\n        writeFile "defaults/foo/bar/v1/.hpack/defaults.yaml" "default-extensions: RecordWildCards"\n        writeFile "defaults/foo/bar/v2/.hpack/defaults.yaml" "default-extensions: DeriveFunctor"\n        [i|\n        defaults:\n          - foo/bar@v1\n          - foo/bar@v2\n        library: {}\n        |] `shouldRenderTo` library_ [i|\n        default-extensions:\n            RecordWildCards\n            DeriveFunctor\n        |]\n\n      it "accepts defaults recursively" $ do\n        writeFile "defaults/foo/bar/v1/.hpack/defaults.yaml" "defaults: foo/bar@v2"\n        writeFile "defaults/foo/bar/v2/.hpack/defaults.yaml" "default-extensions: DeriveFunctor"\n        [i|\n        defaults: foo/bar@v1\n        library: {}\n        |] `shouldRenderTo` library_ [i|\n        default-extensions:\n            DeriveFunctor\n        |]\n\n      it "fails on cyclic defaults" $ do\n        let\n          file1 = "defaults/foo/bar/v1/.hpack/defaults.yaml"\n          file2 = "defaults/foo/bar/v2/.hpack/defaults.yaml"\n        writeFile file1 "defaults: foo/bar@v2"\n        writeFile file2 "defaults: foo/bar@v1"\n        canonic1 <- canonicalizePath file1\n        canonic2 <- canonicalizePath file2\n        [i|\n        defaults: foo/bar@v1\n        library: {}\n        |] `shouldFailWith` [i|cycle in defaults (#{canonic1} -> #{canonic2} -> #{canonic1})|]\n\n      it "fails if defaults don\'t exist" $ do\n        pending\n        [i|\n        defaults:\n          github: sol/foo\n          ref: bar\n        library: {}\n        |] `shouldFailWith` "Invalid value for \\"defaults\\"! File https://raw.githubusercontent.com/sol/foo/bar/.hpack/defaults.yaml does not exist!"\n\n      it "fails on parse error" $ do\n        let file = joinPath ["defaults", "sol", "hpack-template", "2017", "defaults.yaml"]\n        writeFile file "[]"\n        [i|\n        defaults:\n          github: sol/hpack-template\n          path: defaults.yaml\n          ref: "2017"\n        library: {}\n        |] `shouldFailWith` (file ++ ": Error while parsing $ - expected Object, but encountered Array")\n\n      it "warns on unknown fields" $ do\n        let file = joinPath ["defaults", "sol", "hpack-template", "2017", "defaults.yaml"]\n        writeFile file "foo: bar"\n        [i|\n        name: foo\n        defaults:\n          github: sol/hpack-template\n          path: defaults.yaml\n          ref: "2017"\n          bar: baz\n        library: {}\n        |] `shouldWarn` [\n            "package.yaml: Ignoring unrecognized field $.defaults.bar"\n          , file ++ ": Ignoring unrecognized field $.foo"\n          ]\n\n      it "accepts defaults from local files" $ do\n        writeFile "defaults/foo.yaml" [i|\n        defaults:\n          local: bar.yaml\n        |]\n\n        writeFile "defaults/bar.yaml" [i|\n        default-extensions:\n          - RecordWildCards\n          - DeriveFunctor\n        |]\n\n        [i|\n        defaults:\n          local: defaults/foo.yaml\n        library: {}\n        |] `shouldRenderTo` library_ [i|\n        default-extensions:\n            RecordWildCards\n            DeriveFunctor\n        |]\n\n    describe "version" $ do\n      it "accepts string" $ do\n        [i|\n        version: 0.1.0\n        |] `shouldRenderTo` (package "") {packageVersion = "0.1.0"}\n\n      it "accepts number" $ do\n        [i|\n        version: 0.1\n        |] `shouldRenderTo` (package [i|\n        |]) {packageVersion = "0.1"}\n\n      it "rejects other values" $ do\n        [i|\n        version: {}\n        |] `shouldFailWith` "package.yaml: Error while parsing $.version - expected Number or String, but encountered Object"\n\n    describe "license" $ do\n      it "accepts cabal-style licenses" $ do\n        [i|\n        license: BSD3\n        |] `shouldRenderTo` (package [i|\n        license: BSD3\n        |])\n\n      it "accepts SPDX licenses" $ do\n        [i|\n        license: BSD-3-Clause\n        |] `shouldRenderTo` (package [i|\n        license: BSD-3-Clause\n        |]) {packageCabalVersion = "2.2"}\n\n      context "with an ambiguous license" $ do\n        it "treats it as a cabal-style license" $ do\n          [i|\n          license: MIT\n          |] `shouldRenderTo` (package [i|\n          license: MIT\n          |])\n\n      context "when cabal-version >= 2.2" $ do\n        it "maps license to SPDX license identifier" $ do\n          [i|\n          license: BSD3\n          library:\n            cxx-options: -Wall\n          |] `shouldRenderTo` (package [i|\n          license: BSD-3-Clause\n          library\n            other-modules:\n                Paths_foo\n            autogen-modules:\n                Paths_foo\n            cxx-options: -Wall\n            default-language: Haskell2010\n          |]) {packageCabalVersion = "2.2"}\n\n        it "doesn\'t touch unknown licenses" $ do\n          [i|\n          license: some-license\n          library:\n            cxx-options: -Wall\n          |] `shouldRenderTo` (package [i|\n          license: some-license\n          library\n            other-modules:\n                Paths_foo\n            autogen-modules:\n                Paths_foo\n            cxx-options: -Wall\n            default-language: Haskell2010\n          |]) {packageCabalVersion = "2.2"}\n\n      context "with a LICENSE file" $ do\n        before_ (writeFile "LICENSE" license) $ do\n          it "infers license" $ do\n            [i|\n            |] `shouldRenderTo` (package [i|\n            license-file: LICENSE\n            license: MIT\n            |])\n\n          context "when license can not be inferred" $ do\n            it "warns" $ do\n              writeFile "LICENSE" "some-licenese"\n              [i|\n              name: foo\n              |] `shouldWarn` ["Inferring license from file LICENSE failed!"]\n\n          context "when license is null" $ do\n            it "does not infer license" $ do\n              [i|\n              license: null\n              |] `shouldRenderTo` (package [i|\n              license-file: LICENSE\n              |])\n\n    describe "build-type" $ do\n      it "accept Simple" $ do\n        [i|\n        build-type: Simple\n        |] `shouldRenderTo` (package "") {packageBuildType = "Simple"}\n\n      it "accept Configure" $ do\n        [i|\n        build-type: Configure\n        |] `shouldRenderTo` (package "") {packageBuildType = "Configure"}\n\n      it "accept Make" $ do\n        [i|\n        build-type: Make\n        |] `shouldRenderTo` (package "") {packageBuildType = "Make"}\n\n      it "accept Custom" $ do\n        [i|\n        build-type: Custom\n        |] `shouldRenderTo` (package "") {packageBuildType = "Custom"}\n\n      it "rejects invalid values" $ do\n        [i|\n        build-type: foo\n        |] `shouldFailWith` "package.yaml: Error while parsing $.build-type - expected one of Simple, Configure, Make, or Custom"\n\n\n    describe "extra-doc-files" $ do\n      it "accepts a list of files" $ do\n        touch "CHANGES.markdown"\n        touch "README.markdown"\n        [i|\n        extra-doc-files:\n          - CHANGES.markdown\n          - README.markdown\n        |] `shouldRenderTo` (package [i|\n        extra-doc-files:\n            CHANGES.markdown\n            README.markdown\n        |]) {packageCabalVersion = "1.18"}\n\n      it "accepts glob patterns" $ do\n        touch "CHANGES.markdown"\n        touch "README.markdown"\n        [i|\n        extra-doc-files:\n          - "*.markdown"\n        |] `shouldRenderTo` (package [i|\n        extra-doc-files:\n            CHANGES.markdown\n            README.markdown\n        |]) {packageCabalVersion = "1.18"}\n\n      it "warns if a glob pattern does not match anything" $ do\n        [i|\n        name: foo\n        extra-doc-files:\n          - "*.markdown"\n        |] `shouldWarn` ["Specified pattern \\"*.markdown\\" for extra-doc-files does not match any files"]\n\n    describe "build-tools" $ do\n      it "adds known build tools to build-tools" $ do\n        [i|\n        executable:\n          build-tools:\n            alex == 0.1.0\n        |] `shouldRenderTo` executable_ "foo" [i|\n        build-tools:\n            alex ==0.1.0\n        |]\n\n      it "adds other build tools to build-tool-depends" $ do\n        [i|\n        executable:\n          build-tools:\n            hspec-discover: 0.1.0\n        |] `shouldRenderTo` (executable_ "foo" [i|\n        build-tool-depends:\n            hspec-discover:hspec-discover ==0.1.0\n        |]) {\n          -- NOTE: We do not set this to 2.0 on purpose, so that the .cabal\n          -- file is compatible with a wider range of Cabal versions!\n          packageCabalVersion = "1.12"\n        }\n\n      it "accepts build-tool-depends as an alias" $ do\n        [i|\n        executable:\n          build-tool-depends:\n            hspec-discover: 0.1.0\n        |] `shouldRenderTo` (executable_ "foo" [i|\n        build-tool-depends:\n            hspec-discover:hspec-discover ==0.1.0\n        |]) {\n          packageCabalVersion = "1.12"\n        , packageWarnings = ["package.yaml: $.executable.build-tool-depends is deprecated, use $.executable.build-tools instead"]\n        }\n\n      context "when the name of a build tool matches an executable from the same package" $ do\n        it "adds it to build-tools" $ do\n          [i|\n          executables:\n            bar:\n              build-tools:\n                - bar\n          |] `shouldRenderTo` executable_ "bar" [i|\n          build-tools:\n              bar\n          |]\n\n        it "gives per-section unqualified names precedence over global qualified names" $ do\n          [i|\n          build-tools:\n            - foo:bar == 0.1.0\n          executables:\n            bar:\n              build-tools:\n                - bar == 0.2.0\n          |] `shouldRenderTo` executable_ "bar" [i|\n          build-tools:\n              bar ==0.2.0\n          |]\n\n        it "gives per-section qualified names precedence over global unqualified names" $ do\n          [i|\n          build-tools:\n            - bar == 0.1.0\n          executables:\n            bar:\n              build-tools:\n                - foo:bar == 0.2.0\n          |] `shouldRenderTo` executable_ "bar" [i|\n          build-tools:\n              bar ==0.2.0\n          |]\n\n      context "when the name of a build tool matches a legacy system build tool" $ do\n        it "adds it to build-tools" $ do\n          [i|\n          executable:\n            build-tools:\n              ghc >= 7.10\n          |] `shouldRenderTo` (executable_ "foo" [i|\n          build-tools:\n              ghc >=7.10\n          |]) { packageWarnings = ["Listing \\"ghc\\" under build-tools is deperecated! Please list system executables under system-build-tools instead!"] }\n\n    describe "system-build-tools" $ do\n      it "adds system build tools to build-tools" $ do\n        [i|\n        executable:\n          system-build-tools:\n            ghc >= 7.10\n        |] `shouldRenderTo` executable_ "foo" [i|\n        build-tools:\n            ghc >=7.10\n        |]\n\n      context "with hpc" $ do\n        it "infers cabal-version 1.14" $ do\n          [i|\n          executable:\n            system-build-tools:\n              hpc\n          |] `shouldRenderTo` (executable_ "foo" [i|\n          build-tools:\n              hpc\n          |]) {packageCabalVersion = "1.14"}\n\n      context "with ghcjs" $ do\n        it "infers cabal-version 1.22" $ do\n          [i|\n          executable:\n            system-build-tools:\n              ghcjs\n          |] `shouldRenderTo` (executable_ "foo" [i|\n          build-tools:\n              ghcjs\n          |]) {packageCabalVersion = "1.22"}\n\n      context "with an unknown system build tool" $ do\n        it "infers cabal-version 2.0" $ do\n          [i|\n          executable:\n            system-build-tools:\n              g++ >= 5.4.0\n          |] `shouldRenderTo` (executable_ "foo" [i|\n          autogen-modules:\n              Paths_foo\n          build-tools:\n              g++ >=5.4.0\n          |]) {packageCabalVersion = "2.0"}\n\n    describe "dependencies" $ do\n      it "accepts single dependency" $ do\n        [i|\n        executable:\n          dependencies: base\n        |] `shouldRenderTo` executable_ "foo" [i|\n        build-depends:\n            base\n        |]\n\n      it "accepts build-depends as an alias" $ do\n        [i|\n        executable:\n          build-depends: base\n        |] `shouldRenderTo` (executable_ "foo" [i|\n        build-depends:\n            base\n        |]) {\n          packageWarnings = ["package.yaml: $.executable.build-depends is deprecated, use $.executable.dependencies instead"]\n        }\n\n      it "accepts dependencies with subcomponents" $ do\n        [i|\n        executable:\n          dependencies: foo:bar\n        |] `shouldRenderTo` (executable_ "foo" [i|\n        autogen-modules:\n            Paths_foo\n        build-depends:\n            foo:bar\n        |]) {packageCabalVersion = "3.0"}\n\n      it "accepts list of dependencies" $ do\n        [i|\n        executable:\n          dependencies:\n            - base\n            - transformers\n        |] `shouldRenderTo` executable_ "foo" [i|\n        build-depends:\n            base\n          , transformers\n        |]\n\n      context "with both global and section specific dependencies" $ do\n        it "combines dependencies" $ do\n          [i|\n          dependencies:\n            - base\n          executable:\n            dependencies: hspec\n          |] `shouldRenderTo` executable_ "foo" [i|\n          build-depends:\n              base\n            , hspec\n          |]\n\n        it "gives section specific dependencies precedence" $ do\n          [i|\n          dependencies:\n            - base\n          executable:\n            dependencies: base >= 2\n          |] `shouldRenderTo` executable_ "foo" [i|\n          build-depends:\n              base >=2\n          |]\n\n    describe "pkg-config-dependencies" $ do\n      it "accepts pkg-config-dependencies" $ do\n        [i|\n        pkg-config-dependencies:\n          - QtWebKit\n          - weston\n        executable: {}\n        |] `shouldRenderTo` executable_ "foo" [i|\n        pkgconfig-depends:\n            QtWebKit\n          , weston\n        |]\n\n      it "accepts pkgconfig-depends as an alias" $ do\n        [i|\n        pkgconfig-depends:\n          - QtWebKit\n          - weston\n        executable: {}\n        |] `shouldRenderTo` executable_ "foo" [i|\n        pkgconfig-depends:\n            QtWebKit\n          , weston\n        |]\n\n    describe "include-dirs" $ do\n      it "accepts include-dirs" $ do\n        [i|\n        include-dirs:\n          - foo\n          - bar\n        executable: {}\n        |] `shouldRenderTo` executable_ "foo" [i|\n        include-dirs:\n            foo\n            bar\n        |]\n\n    describe "install-includes" $ do\n      it "accepts install-includes" $ do\n        [i|\n        install-includes:\n          - foo.h\n          - bar.h\n        executable: {}\n        |] `shouldRenderTo` executable_ "foo" [i|\n        install-includes:\n            foo.h\n            bar.h\n        |]\n\n    describe "js-sources" $ before_ (touch "foo.js" >> touch "jsbits/bar.js") $ do\n      it "accepts js-sources" $ do\n        [i|\n        executable:\n          js-sources:\n            - foo.js\n            - jsbits/*.js\n        |] `shouldRenderTo` executable_ "foo" [i|\n        js-sources:\n            foo.js\n            jsbits/bar.js\n        |]\n\n      it "accepts global js-sources" $ do\n        [i|\n        js-sources:\n          - foo.js\n          - jsbits/*.js\n        executable: {}\n        |] `shouldRenderTo` executable_ "foo" [i|\n        js-sources:\n            foo.js\n            jsbits/bar.js\n        |]\n\n    describe "cxx-options" $ do\n      it "accepts cxx-options" $ do\n        [i|\n        executable:\n          cxx-options: -Wall\n        |] `shouldRenderTo` (executable_ "foo" [i|\n        autogen-modules:\n            Paths_foo\n        cxx-options: -Wall\n        |]) {packageCabalVersion = "2.2"}\n\n      context "when used inside a nested conditional" $ do\n        it "infers correct cabal-version" $ do\n          [i|\n          executable:\n            when:\n              condition: True\n              when:\n                condition: True\n                when:\n                  condition: True\n                  cxx-options: -Wall\n          |] `shouldRenderTo` (executable "foo" [i|\n          other-modules:\n              Paths_foo\n          autogen-modules:\n              Paths_foo\n          default-language: Haskell2010\n          if true\n            if true\n              if true\n                cxx-options: -Wall\n          |]) {packageCabalVersion = "2.2"}\n\n    describe "cxx-sources" $ before_ (touch "foo.cc" >> touch "cxxbits/bar.cc") $ do\n      it "accepts cxx-sources" $ do\n        [i|\n        executable:\n          cxx-sources:\n            - foo.cc\n            - cxxbits/*.cc\n        |] `shouldRenderTo` (executable_ "foo" [i|\n        autogen-modules:\n            Paths_foo\n        cxx-sources:\n            foo.cc\n            cxxbits/bar.cc\n        |]) {packageCabalVersion = "2.2"}\n\n    describe "language" $ do\n      it "accepts language" $ do\n        [i|\n        language: GHC2021\n        executable: {}\n        |] `shouldRenderTo` executable "foo" [i|\n          other-modules:\n              Paths_foo\n          default-language: GHC2021\n        |]\n\n      it "omits language if it is null" $ do\n        [i|\n        language: null\n        executable: {}\n        |] `shouldRenderTo` executable "foo" [i|\n          other-modules:\n              Paths_foo\n        |]\n\n      it "accepts default-language as an alias" $ do\n        [i|\n        default-language: GHC2021\n        executable: {}\n        |] `shouldRenderTo` (executable "foo" [i|\n          other-modules:\n              Paths_foo\n          default-language: GHC2021\n              Foo\n          |]\n\n      context "when inferring modules" $ do\n        it "ignores duplicate source directories" $ do\n          touch "src/Foo.hs"\n          [i|\n    describe "frameworks" $ do\n      it "accepts frameworks" $ do\n        [i|\n        frameworks:\n          - foo\n          - bar\n        executable: {}\n        |] `shouldRenderTo` executable_ "foo" [i|\n        frameworks:\n            foo\n            bar\n        |]\n\n    describe "c-sources" $ before_ (touch "cbits/foo.c" >> touch "cbits/bar.c" >> touch "cbits/baz.c") $ do\n      it "keeps declaration order" $ do\n        -- IMPORTANT: This is crucial as a workaround for https://ghc.haskell.org/trac/ghc/ticket/13786\n        [i|\n        library:\n          c-sources:\n            - cbits/foo.c\n            - cbits/bar.c\n            - cbits/baz.c\n        |] `shouldRenderTo` library_ [i|\n        c-sources:\n            cbits/foo.c\n            cbits/bar.c\n            cbits/baz.c\n        |]\n\n      it "accepts glob patterns" $ do\n        [i|\n        library:\n          c-sources: cbits/*.c\n        |] `shouldRenderTo` library_ [i|\n        c-sources:\n            cbits/bar.c\n            cbits/baz.c\n            cbits/foo.c\n        |]\n\n      it "warns when a glob pattern does not match any files" $ do\n        [i|\n        name: foo\n        library:\n          c-sources: foo/*.c\n        |] `shouldWarn` pure "Specified pattern \\"foo/*.c\\" for c-sources does not match any files"\n\n      it "quotes filenames with special characters" $ do\n        touch "cbits/foo bar.c"\n        [i|\n        library:\n          c-sources:\n            - cbits/foo bar.c\n        |] `shouldRenderTo` library_ [i|\n        c-sources:\n            "cbits/foo bar.c"\n        |]\n\n    describe "custom-setup" $ do\n      it "warns on unknown fields" $ do\n        [i|\n        name: foo\n        custom-setup:\n          foo: 1\n          bar: 2\n        |] `shouldWarn` [\n            "package.yaml: Ignoring unrecognized field $.custom-setup.bar"\n          , "package.yaml: Ignoring unrecognized field $.custom-setup.foo"\n          ]\n\n      it "accepts dependencies" $ do\n        [i|\n        custom-setup:\n          dependencies:\n            - base\n        |] `shouldRenderTo` customSetup [i|\n        setup-depends:\n            base\n        |]\n\n      it "leaves build-type alone, if it exists" $ do\n        [i|\n        build-type: Make\n        custom-setup:\n          dependencies:\n            - base\n        |] `shouldRenderTo` (customSetup [i|\n        setup-depends:\n            base\n        |]) {packageBuildType = "Make"}\n\n    describe "library" $ do\n      it "accepts reexported-modules" $ do\n        [i|\n        library:\n          reexported-modules: Baz\n        |] `shouldRenderTo` (library_ [i|\n          reexported-modules:\n              Baz\n        |]) {packageCabalVersion = "1.22"}\n\n      it "accepts signatures" $ do\n        [i|\n        library:\n          signatures: Foo\n        |] `shouldRenderTo` (library_ [i|\n          autogen-modules:\n              Paths_foo\n          signatures:\n              Foo\n        |]) {packageCabalVersion = "2.0"}\n\n      context "when package.yaml contains duplicate modules" $ do\n        it "generates a cabal file with duplicate modules" $ do\n          -- garbage in, garbage out\n          [i|\n          library:\n            exposed-modules: Foo\n            other-modules: Foo\n          |] `shouldRenderTo` library [i|\n          exposed-modules:\n              Foo\n          other-modules:\n              Foo\n          default-language: Haskell2010\n          |]\n\n      context "with mixins" $ do\n        it "infers cabal-version 2.0" $ do\n          [i|\n          library:\n            dependencies:\n              foo:\n                mixin:\n                  - (Blah as Etc)\n          |] `shouldRenderTo` (library [i|\n          other-modules:\n              Paths_foo\n          autogen-modules:\n              Paths_foo\n          build-depends:\n              foo\n          mixins:\n              foo (Blah as Etc)\n          default-language: Haskell2010\n          |]) {packageCabalVersion = "2.0"}\n\n    describe "internal-libraries" $ do\n      it "accepts internal-libraries" $ do\n        touch "src/Foo.hs"\n        [i|\n        internal-libraries:\n          bar:\n            source-dirs: src\n        |] `shouldRenderTo` internalLibrary "bar" [i|\n        exposed-modules:\n            Foo\n        other-modules:\n            Paths_foo\n        autogen-modules:\n            Paths_foo\n        hs-source-dirs:\n            src\n        |]\n\n      it "warns on unknown fields" $ do\n        [i|\n        name: foo\n        internal-libraries:\n          bar:\n            baz: 42\n        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"\n\n      it "warns on missing source-dirs" $ do\n        [i|\n        name: foo\n        internal-libraries:\n          bar:\n            source-dirs: src\n        |] `shouldWarn` pure "Specified source-dir \\"src\\" does not exist"\n\n      it "accepts visibility" $ do\n        [i|\n        internal-libraries:\n          bar:\n            visibility: public\n        |] `shouldRenderTo` (internalLibrary "bar" [i|\n        visibility: public\n        other-modules:\n            Paths_foo\n        autogen-modules:\n            Paths_foo\n        |]) {packageCabalVersion = "3.0"}\n\n    context "when inferring modules" $ do\n      context "with a library" $ do\n        it "ignores duplicate source directories" $ do\n          touch "src/Foo.hs"\n          [i|\n          source-dirs: src\n          library:\n            source-dirs: src\n          |] `shouldRenderTo` library [i|\n          hs-source-dirs:\n              src\n          exposed-modules:\n              Foo\n          other-modules:\n              Paths_foo\n          default-language: Haskell2010\n          |]\n\n        it "ignores duplicate modules" $ do\n          touch "src/Foo.hs"\n          touch "src/Foo.x"\n          [i|\n          library:\n            source-dirs: src\n          |] `shouldRenderTo` library [i|\n          hs-source-dirs:\n              src\n          exposed-modules:\n              Foo\n          other-modules:\n                  Exposed\n            |]) {packageCabalVersion = "2.0"}\n\n      context "with mixins" $ do\n        it "infers cabal-version 2.0" $ do\n          [i|\n          library:\n            dependencies:\n              foo:\n                mixin:\n                  - (Blah as Etc)\n          |] `shouldRenderTo` (library [i|\n          other-modules:\n              Paths_foo\n          build-depends:\n              foo\n          mixins:\n              foo (Blah as Etc)\n          |]) {packageCabalVersion = "2.0"}\n\n    describe "internal-libraries" $ do\n      it "accepts internal-libraries" $ do\n        touch "src/Foo.hs"\n        [i|\n        internal-libraries:\n          bar:\n            source-dirs: src\n        |] `shouldRenderTo` internalLibrary "bar" [i|\n        exposed-modules:\n            Foo\n        other-modules:\n            Paths_foo\n        hs-source-dirs:\n            src\n        |]\n\n      it "warns on unknown fields" $ do\n        [i|\n        name: foo\n        internal-libraries:\n          bar:\n            baz: 42\n        |] `shouldWarn` pure "package.yaml: Ignoring unrecognized field $.internal-libraries.bar.baz"\n\n      it "warns on missing source-dirs" $ do\n        [i|\n        name: foo\n        internal-libraries:\n          bar:\n            source-dirs: src\n        |] `shouldWarn` pure "Specified source-dir \\"src\\" does not exist"\n\n      it "accepts visibility" $ do\n        [i|\n        internal-libraries:\n          bar:\n            visibility: public\n        |] `shouldRenderTo` (internalLibrary "bar" [i|\n        visibility: public\n        other-modules:\n            Paths_foo\n        |]) {packageCabalVersion = "3.0"}\n\n    describe "executables" $ do\n      it "accepts arbitrary entry points as main" $ do\n        touch "src/Foo.hs"\n        touch "src/Bar.hs"\n        [i|\n        executables:\n          foo:\n            source-dirs: src\n            main: Foo\n        |] `shouldRenderTo` executable "foo" [i|\n        main-is: Foo.hs\n        ghc-options: -main-is Foo\n        hs-source-dirs:\n            src\n        other-modules:\n            Bar\n            Paths_foo\n        |]\n\n      context "when inferring modules" $ do\n        it "infers other-modules" $ do\n          touch "src/Main.hs"\n          touch "src/Foo.hs"\n            touch "src/Bar.hs"\n            [i|\n            library:\n              source-dirs: src\n              exposed-modules: Foo\n            |] `shouldRenderTo` library [i|\n            hs-source-dirs:\n                src\n            exposed-modules:\n                Foo\n            other-modules:\n                Bar\n                Paths_foo\n            default-language: Haskell2010\n            |]\n\n        context "with other-modules" $ do\n          it "infers exposed-modules" $ do\n            touch "src/Foo.hs"\n            touch "src/Bar.hs"\n            [i|\n            library:\n              source-dirs: src\n              other-modules: Bar\n            |] `shouldRenderTo` library [i|\n            hs-source-dirs:\n                src\n            exposed-modules:\n                Foo\n            other-modules:\n                Bar\n            default-language: Haskell2010\n            |]\n\n        context "with both exposed-modules and other-modules" $ do\n          it "doesn\'t infer any modules" $ do\n            touch "src/Foo.hs"\n            touch "src/Bar.hs"\n            [i|\n            library:\n              source-dirs: src\n              exposed-modules: Foo\n              other-modules: Bar\n            |] `shouldRenderTo` library [i|\n            hs-source-dirs:\n                src\n            exposed-modules:\n                Foo\n          |]) {packageCabalVersion = "2.0"}\n\n        context "with conditional" $ do\n          it "doesn\'t infer any modules mentioned in that conditional" $ do\n            touch "src/Foo.hs"\n            touch "src/Bar.hs"\n          it "infers exposed-modules" $ do\n            touch "src/Foo.hs"\n            touch "src/Bar.hs"\n            [i|\n            library:\n              source-dirs: src\n            |] `shouldRenderTo` library [i|\n            hs-source-dirs:\n                src\n            exposed-modules:\n                Bar\n                Foo\n            other-modules:\n                Paths_foo\n            default-language: Haskell2010\n            |]\n\n        context "with a conditional" $ do\n          it "doesn\'t infer any modules mentioned in that conditional" $ do\n            touch "src/Foo.hs"\n            touch "src/Bar.hs"\n            [i|\n            library:\n              source-dirs: src\n              when:\n                condition: os(windows)\n                exposed-modules:\n                  - Foo\n                  - Paths_foo\n            |] `shouldRenderTo` package [i|\n            library\n              hs-source-dirs:\n                  src\n              exposed-modules:\n                  Bar\n              default-language: Haskell2010\n              if os(windows)\n                exposed-modules:\n                  windows\n            |]\n\n      context "with conditional" $ do\n        it "does not apply global options" $ do\n          -- related bug: https://github.com/sol/hpack/issues/214\n          [i|\n              touch "unix/Foo.hs"\n              [i|\n              library:\n                exposed-modules: Foo\n                when:\n                  condition: os(windows)\n                  then:\n                    source-dirs: windows/\n                  else:\n                    source-dirs: unix/\n\n              |] `shouldRenderTo` package [i|\n              library\n                exposed-modules:\n                    Foo\n                other-modules:\n                    Paths_foo\n                default-language: Haskell2010\n                if os(windows)\n                  hs-source-dirs:\n                      windows/\n                else\n                  hs-source-dirs:\n                      unix/\n              |]\n\n        context "with generated modules" $ do\n          it "includes generated modules in autogen-modules" $ do\n            [i|\n            library:\n              generated-exposed-modules: Foo\n              generated-other-modules: Bar\n            |] `shouldRenderTo` (library [i|\n            exposed-modules:\n                Foo\n            other-modules:\n                Paths_foo\n                Bar\n            autogen-modules:\n                Paths_foo\n                Foo\n                Bar\n            default-language: Haskell2010\n            |]) {packageCabalVersion = "2.0"}\n\n          it "does not infer any mentioned generated modules" $ do\n            touch "src/Exposed.hs"\n            touch "src/Other.hs"\n            [i|\n            library:\n              source-dirs: src\n              generated-exposed-modules: Exposed\n              generated-other-modules: Other\n            |] `shouldRenderTo` (library [i|\n            hs-source-dirs:\n                src\n            exposed-modules:\n                Exposed\n            other-modules:\n                Paths_foo\n                Other\n            autogen-modules:\n                Paths_foo\n                Exposed\n                Other\n            default-language: Haskell2010\n            |]) {packageCabalVersion = "2.0"}\n\n          it "does not infer any generated modules mentioned inside conditionals" $ do\n            touch "src/Exposed.hs"\n            touch "src/Other.hs"\n            [i|\n            library:\n              source-dirs: src\n              when:\n                condition: os(windows)\n                generated-exposed-modules: Exposed\n                generated-other-modules: Other\n            |] `shouldRenderTo` (package [i|\n            library\n              other-modules:\n                  Paths_foo\n              autogen-modules:\n                  Paths_foo\n              hs-source-dirs:\n                  src\n              default-language: Haskell2010\n              if os(windows)\n                exposed-modules:\n                    Exposed\n                other-modules:\n                    Other\n                autogen-modules:\n                    Other\n                    Exposed\n            |]) {packageCabalVersion = "2.0"}\n\n      context "with an executable" $ do\n        it "infers other-modules" $ do\n          touch "src/Main.hs"\n          touch "src/Foo.hs"\n          [i|\n          executables:\n            foo:\n              main: Main.hs\n              source-dirs: src\n          |] `shouldRenderTo` executable "foo" [i|\n            main-is: Main.hs\n            hs-source-dirs:\n                src\n            other-modules:\n                Foo\n                Paths_foo\n            default-language: Haskell2010\n          |]\n\n        it "allows to specify other-modules" $ do\n          touch "src/Foo.hs"\n          touch "src/Bar.hs"\n          [i|\n          executables:\n            foo:\n              main: Main.hs\n              source-dirs: src\n              other-modules: Baz\n          |] `shouldRenderTo` executable "foo" [i|\n            main-is: Main.hs\n            hs-source-dirs:\n                src\n            other-modules:\n                Baz\n            default-language: Haskell2010\n          |]\n\n        it "does not infer any mentioned generated modules" $ do\n          touch "src/Foo.hs"\n          [i|\n          executables:\n            foo:\n              main: Main.hs\n              source-dirs: src\n              generated-other-modules: Foo\n          |] `shouldRenderTo` (executable "foo" [i|\n            main-is: Main.hs\n            hs-source-dirs:\n                src\n            other-modules:\n                Paths_foo\n                Foo\n            autogen-modules:\n                Paths_foo\n                Foo\n            default-language: Haskell2010\n          |]) {packageCabalVersion = "2.0"}\n\n        context "with a conditional" $ do\n          it "doesn\'t infer any modules mentioned in that conditional" $ do\n            touch "src/Foo.hs"\n            touch "src/Bar.hs"\n            [i|\n            executables:\n              foo:\n                source-dirs: src\n                when:\n                  condition: os(windows)\n                  other-modules: Foo\n            |] `shouldRenderTo` executable "foo" [i|\n            other-modules:\n                Bar\n                Paths_foo\n            hs-source-dirs:\n                src\n            default-language: Haskell2010\n            if os(windows)\n              other-modules:\n                  Foo\n            |]\n\n          it "infers other-modules" $ do\n            touch "src/Foo.hs"\n            touch "windows/Bar.hs"\n            [i|\n            executables:\n              foo:\n                source-dirs: src\n                when:\n                  condition: os(windows)\n                  source-dirs: windows\n            |] `shouldRenderTo` executable "foo" [i|\n            other-modules:\n                Foo\n                Paths_foo\n            hs-source-dirs:\n                src\n            default-language: Haskell2010\n            if os(windows)\n              other-modules:\n                  Bar\n              hs-source-dirs:\n                  windows\n            |]\n\n    describe "executables" $ do\n      it "accepts main-is as an alias for main" $ do\n        [i|\n        executable:\n          main-is: Foo.hs\n        |] `shouldRenderTo` (executable_ "foo" [i|\n        main-is: Foo.hs\n        |]) {\n          packageWarnings = ["package.yaml: $.executable.main-is is deprecated, use $.executable.main instead"]\n        }\n\n      it "accepts arbitrary entry points as main" $ do\n        touch "src/Foo.hs"\n        touch "src/Bar.hs"\n        [i|\n        executables:\n          foo:\n            source-dirs: src\n            main: Foo\n        |] `shouldRenderTo` executable "foo" [i|\n        main-is: Foo.hs\n        ghc-options: -main-is Foo\n        hs-source-dirs:\n            src\n        other-modules:\n            Bar\n            Paths_foo\n        default-language: Haskell2010\n        |]\n\n      context "with a conditional" $ do\n        it "does not apply global options" $ do\n          -- related bug: https://github.com/sol/hpack/issues/214\n          [i|\n          ghc-options: -Wall\n          executables:\n            foo:\n              when:\n                condition: os(windows)\n                main: Foo.hs\n          |] `shouldRenderTo` executable "foo" [i|\n          ghc-options: -Wall\n          other-modules:\n              Paths_foo\n          default-language: Haskell2010\n          if os(windows)\n            main-is: Foo.hs\n          |]\n\n        it "accepts executable-specific fields" $ do\n          [i|\n          executables:\n            foo:\n              when:\n                condition: os(windows)\n                main: Foo\n          |] `shouldRenderTo` executable "foo" [i|\n          other-modules:\n              Paths_foo\n          default-language: Haskell2010\n          if os(windows)\n            main-is: Foo.hs\n            ghc-options: -main-is Foo\n          |]\n\n    describe "when" $ do\n      it "accepts conditionals" $ do\n        [i|\n        when:\n          condition: os(windows)\n          dependencies: Win32\n        executable: {}\n        |] `shouldRenderTo` executable "foo" [i|\n        other-modules:\n            Paths_foo\n        default-language: Haskell2010\n        if os(windows)\n          build-depends:\n              Win32\n        |]\n      it "warns on unknown fields" $ do\n        [i|\n        name: foo\n        foo: 23\n        when:\n          - condition: os(windows)\n            bar: 23\n            when:\n              condition: os(windows)\n              bar2: 23\n          - condition: os(windows)\n            baz: 23\n        |] `shouldWarn` [\n            "package.yaml: Ignoring unrecognized field $.foo"\n          , "package.yaml: Ignoring unrecognized field $.when[0].bar"\n          , "package.yaml: Ignoring unrecognized field $.when[0].when.bar2"\n          , "package.yaml: Ignoring unrecognized field $.when[1].baz"\n          ]\n\n      context "when parsing conditionals with else-branch" $ do\n        it "accepts conditionals with else-branch" $ do\n          [i|\n          when:\n            condition: os(windows)\n            then:\n              dependencies: Win32\n            else:\n              dependencies: unix\n          executable: {}\n          |] `shouldRenderTo` executable "foo" [i|\n          other-modules:\n              Paths_foo\n          default-language: Haskell2010\n          if os(windows)\n            build-depends:\n                Win32\n          else\n            build-depends:\n                unix\n          |]\n\n        context "with empty then-branch" $ do\n          it "provides a hint" $ do\n            [i|\n            when:\n              condition: os(windows)\n              then: {}\n              else:\n                dependencies: unix\n            executable: {}\n            |] `shouldFailWith` unlines [\n                "package.yaml: Error while parsing $.when - an empty \\"then\\" section is not allowed, try the following instead:"\n              , ""\n              , "when:"\n              , "  condition: \'!(os(windows))\'"\n              , "  dependencies: unix"\n              ]\n\n        context "with empty else-branch" $ do\n          it "provides a hint" $ do\n            [i|\n            when:\n              condition: os(windows)\n              then:\n                dependencies: Win32\n              else: {}\n            executable: {}\n            |] `shouldFailWith` unlines [\n                "package.yaml: Error while parsing $.when - an empty \\"else\\" section is not allowed, try the following instead:"\n              , ""\n              , "when:"\n              , "  condition: os(windows)"\n              , "  dependencies: Win32"\n              ]\n\n        it "rejects invalid conditionals" $ do\n          [i|\n          when:\n            condition: os(windows)\n            then:\n              dependencies: Win32\n            else: null\n          |] `shouldFailWith` "package.yaml: Error while parsing $.when.else - expected Object, but encountered Null"\n\n        it "rejects invalid conditionals" $ do\n          [i|\n            dependencies:\n              - foo\n              - 23\n          |] `shouldFailWith` "package.yaml: Error while parsing $.dependencies[1] - expected Object or String, but encountered Number"\n\n        it "warns on unknown fields" $ do\n          [i|\n          name: foo\n          when:\n            condition: os(windows)\n            foo: null\n            then:\n              bar: null\n            else:\n              when:\n                condition: os(windows)\n                then:\n                  dependencies: foo\n                else:\n                  baz: null\n          |] `shouldWarn` [\n              "package.yaml: Ignoring unrecognized field $.when.foo"\n            , "package.yaml: Ignoring unrecognized field $.when.then.bar"\n            , "package.yaml: Ignoring unrecognized field $.when.else.when.else.baz"\n            ]\n\n    describe "verbatim" $ do\n      it "accepts strings" $ do\n        [i|\n        library:\n          verbatim: |\n            foo: 23\n            bar: 42\n        |] `shouldRenderTo` package [i|\n        library\n          other-modules:\n              Paths_foo\n          default-language: Haskell2010\n          foo: 23\n          bar: 42\n        |]\n\n      it "accepts multi-line strings as field values" $ do\n        [i|\n        library:\n          verbatim:\n            build-depneds: |\n              foo\n              bar\n              baz\n        |] `shouldRenderTo` package [i|\n        library\n          other-modules:\n              Paths_foo\n          default-language: Haskell2010\n          build-depneds:\n              foo\n              bar\n              baz\n        |]\n\n      it "allows to null out existing fields" $ do\n        [i|\n        library:\n          verbatim:\n            default-language: null\n        |] `shouldRenderTo` package [i|\n        library\n          other-modules:\n              Paths_foo\n        |]\n\n      context "when specified globally" $ do\n        it "overrides header fields" $ do\n          [i|\n          verbatim:\n            cabal-version: foo\n          |] `shouldRenderTo` (package "") {packageCabalVersion = "foo"}\n\n        it "overrides other fields" $ do\n          touch "foo"\n          [i|\n          extra-source-files: foo\n          verbatim:\n            extra-source-files: bar\n          |] `shouldRenderTo` package [i|\n          extra-source-files: bar\n          |]\n\n        it "is not propagated into sections" $ do\n          [i|\n          verbatim:\n            foo: 23\n          library: {}\n          |] `shouldRenderTo` package [i|\n          foo: 23\n          library\n            other-modules:\n                Paths_foo\n            default-language: Haskell2010\n          |]\n\n      context "within a section" $ do\n        it "overrides section fields" $ do\n          [i|\n          tests:\n            spec:\n              verbatim:\n                type: detailed-0.9\n          |] `shouldRenderTo` package [i|\n          test-suite spec\n            type: detailed-0.9\n            other-modules:\n                Paths_foo\n            default-language: Haskell2010\n          |]\n    describe "default value of maintainer" $ do\n      it "gives maintainer precedence" $ do\n        [i|\n          author: John Doe\n          maintainer: Jane Doe\n          |] `shouldRenderTo` package [i|\n          author: John Doe\n          maintainer: Jane Doe\n          |]\n      context "with author" $ do\n        it "uses author if maintainer is not specified" $ do\n          [i|\n            author: John Doe\n            |] `shouldRenderTo` package [i|\n            author: John Doe\n            maintainer: John Doe\n            |]\n        it "omits maintainer if it is null" $ do\n          [i|\n            author: John Doe\n            maintainer: null\n            |] `shouldRenderTo` package [i|\n            author: John Doe\n            |]\n\nrun :: HasCallStack => FilePath -> FilePath -> String -> IO ([String], String)\nrun userDataDir c old = run_ userDataDir c old >>= either assertFailure return\n\nrun_ :: FilePath -> FilePath -> String -> IO (Either String ([String], String))\nrun_ userDataDir c old = do\n  mPackage <- readPackageConfig defaultDecodeOptions {decodeOptionsTarget = c, decodeOptionsUserDataDir = Just userDataDir}\n  return $ case mPackage of\n    Right (DecodeResult pkg cabalVersion _ warnings) ->\n      let\n        FormattingHints{..} = sniffFormattingHints (lines old)\n        alignment = fromMaybe 0 formattingHintsAlignment\n        settings = formattingHintsRenderSettings\n        output = cabalVersion ++ Hpack.renderPackageWith settings alignment formattingHintsFieldOrder formattingHintsSectionsFieldOrder pkg\n      in\n        Right (warnings, output)\n    Left err -> Left err\n\ndata RenderResult = RenderResult [String] String\n  deriving Eq\n\ninstance Show RenderResult where\n  show (RenderResult warnings output) = unlines (map ("WARNING: " ++) warnings) ++ output\n\nshouldRenderTo :: HasCallStack => String -> Package -> Expectation\nshouldRenderTo input p = do\n  writeFile packageConfig ("name: foo\\n" ++ unindent input)\n  (warnings, output) <- run "" packageConfig expected\n  RenderResult warnings (dropEmptyLines output) `shouldBe` RenderResult (packageWarnings p) expected\n  where\n    expected = dropEmptyLines (renderPackage p)\n    dropEmptyLines = unlines . filter (not . null) . lines\n\nshouldWarn :: HasCallStack => String -> [String] -> Expectation\nshouldWarn input expected = do\n  writeFile packageConfig input\n  (warnings, _) <- run "" packageConfig ""\n  sort warnings `shouldBe` sort expected\n\nshouldFailWith :: HasCallStack => String -> String -> Expectation\nshouldFailWith input expected = do\n  writeFile packageConfig input\n  run_ "" packageConfig "" `shouldReturn` Left expected\n\ncustomSetup :: String -> Package\ncustomSetup a = (package content) {packageCabalVersion = "1.24", packageBuildType = "Custom"}\n  where\n    content = [i|\ncustom-setup\n#{indentBy 2 $ unindent a}\n|]\n\nlibrary_ :: String -> Package\nlibrary_ l = package content\n  where\n    content = [i|\nlibrary\n  other-modules:\n      Paths_foo\n#{indentBy 2 $ unindent l}\n  default-language: Haskell2010\n|]\n\nlibrary :: String -> Package\nlibrary l = package content\n  where\n    content = [i|\nlibrary\n#{indentBy 2 $ unindent l}\n|]\n\ninternalLibrary :: String -> String -> Package\ninternalLibrary name e = (package content) {packageCabalVersion = "2.0"}\n  where\n    content = [i|\nlibrary #{name}\n#{indentBy 2 $ unindent e}\n  default-language: Haskell2010\n|]\n\nexecutable_ :: String -> String -> Package\nexecutable_ name e = package content\n  where\n    content = [i|\nexecutable #{name}\n  other-modules:\n      Paths_foo\n#{indentBy 2 $ unindent e}\n  default-language: Haskell2010\n|]\n\nexecutable :: String -> String -> Package\nexecutable name e = package content\n  where\n    content = [i|\nexecutable #{name}\n#{indentBy 2 $ unindent e}\n|]\n\npackage :: String -> Package\npackage c = Package "foo" "0.0.0" "Simple" "1.12" c []\n\ndata Package = Package {\n  packageName :: String\n, packageVersion :: String\n, packageBuildType :: String\n, packageCabalVersion :: String\n, packageContent :: String\n, packageWarnings :: [String]\n}\n\nrenderPackage :: Package -> String\nrenderPackage Package{..} = unindent [i|\ncabal-version: #{packageCabalVersion}\nname: #{packageName}\nversion: #{packageVersion}\nbuild-type: #{packageBuildType}\n\n#{unindent packageContent}\n|]\n\nindentBy :: Int -> String -> String\nindentBy n = unlines . map (replicate n \' \' ++) . lines\n\nlicense :: String\nlicense = [i|\nCopyright (c) 2014-2018 Simon Hengel <sol@typeful.net>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the "Software"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n|]\n', 'message': 'Minor refactoring\n', 'author_name': 'Simon Hengel', 'lang': 'hs'}