packages feed

cabal2nix-2.19.0: test/Main.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE LambdaCase #-}

module Main ( main ) where

import Distribution.Nixpkgs.Fetch
import Distribution.Nixpkgs.Haskell.Derivation
import Distribution.Nixpkgs.Haskell.FromCabal
import Distribution.Nixpkgs.Haskell.FromCabal.Flags

import Control.Lens
import Control.Monad
import qualified Data.Set as Set
import Distribution.Compiler
import Distribution.Package
import Distribution.PackageDescription
import Distribution.PackageDescription.Parsec
import Distribution.System
import Distribution.Verbosity
import Distribution.Version
import Language.Nix
import System.Directory
import System.FilePath
import System.Process
import Test.Tasty
import Test.Tasty.Golden
import Text.PrettyPrint.HughesPJClass

main :: IO ()
main = do
  -- TODO: Run this test with all kinds of setLocaleEncoding values to ensure we don't
  --       depend on the system environment: https://github.com/NixOS/cabal2nix/issues/333.
  --
  -- TODO: Run this test without $HOME defined to ensure that we don't need that variable.
  cabal2nix <- findExecutable "cabal2nix" >>= \case
    Nothing -> fail "cannot find 'cabal2nix' in $PATH"
    Just exe -> pure exe
  testCases <- listDirectoryFilesBySuffix ".cabal" "test/golden-test-cases"
  defaultMain $ testGroup "regression-tests"
    [ testGroup "cabal2nix library" (map testLibrary testCases)
    , testGroup "cabal2nix executable" (map (testExecutable cabal2nix) testCases)
    ]

testLibrary :: String -> TestTree
testLibrary cabalFile = do
  let nixFile = cabalFile `replaceExtension` "nix"
      goldenFile = nixFile `addExtension` "golden"

      cabal2nix :: GenericPackageDescription -> Derivation
      cabal2nix gpd = fromGenericPackageDescription
                         (const True)
                         (\i -> Just (binding # (i, path # [ident # "pkgs", i])))
                         (Platform X86_64 Linux)
                         (unknownCompilerInfo (CompilerId GHC (mkVersion [8,2])) NoAbiTag)
                         (configureCabalFlags (packageId gpd))
                         []
                         gpd
                       & src .~ DerivationSource
                                  { derivKind     = Just (DerivKindUrl DontUnpackArchive )
                                  , derivUrl      = "mirror://hackage/foo.tar.gz"
                                  , derivRevision = ""
                                  , derivHash     = "deadbeef"
                                  , derivSubmodule = Nothing
                                  }
                       & extraFunctionArgs %~ Set.union (Set.singleton "inherit lib")
  goldenVsFileDiff
    nixFile
    (\ref new -> ["diff", "-u", ref, new])
    goldenFile
    nixFile
    (readGenericPackageDescription silent cabalFile >>= writeFileLn nixFile . prettyShow . cabal2nix)

-- | TODO:
--
--  * Run cabal2nix in an empty environment. Currently, this fails because the
--    tool uses $HOME to write cache files for SHA256 hashes. To fix this, use
--    of the cache directory ought to be optional. Arguably, the whole notion
--    of caching those hashes is a bit silly in the presence of format 01
--    cabal-install tarballs which contain them already.
--
--  * If we cannot get along without $HOME, then we should at least set it to
--    some temporary location that is empty.
--
--  * Run this test with different kinds of encodings.
--
--  * Run tests with a different target OS, like Darwin. The packages "iconv",
--    "crypt-sha512", "gl" might be worthwhile for testing since their output
--    differs on Darwin.

testExecutable :: FilePath -> FilePath -> TestTree
testExecutable exe cabalFile = do
  let nixFile = cabalFile `replaceExtension` "nix"
      goldenFile = nixFile `addExtension` "golden"
  goldenVsFileDiff
    nixFile
    (\ref new -> ["diff", "-u", ref, new])
    goldenFile
    nixFile
    (callCommand (unwords ["env LANG=C.UTF-8", exe, "--sha256=deadbeef", "--system=x86_64-linux", "--compiler=ghc-8.2", "--", cabalFile, ">"++nixFile]))

-------------------------------------------------------------------------------
-- * Helper functions
-------------------------------------------------------------------------------

-- | Find all *files* in the given directory that end with the given suffix.
-- This function does not recurse, nor does it include the special entries
-- @'.'@ or @'..'@ in the result.
--
-- "Test.Tasty.Golden" offers 'findByExtension', which does something similar
-- (and arguably more general) as those two functions, but unfortunately that
-- function recurses through the directory hierarchy and in the process needs
-- plenty of stack space. There might be a space leak in that code, I am not
-- sure. Anyhow, since our test suite is supposed to run with 1K stack space,
-- we prefer our own simpler code.

listDirectoryFilesBySuffix :: String -> FilePath -> IO [FilePath]
listDirectoryFilesBySuffix suff dirPath = do
  entries <- listDirectory dirPath
  filterM doesFileExist [ dirPath </> e | e <- entries, takeExtension e == suff ]

-- | A variant of 'writeFile' that appends a newline to the end of the buffer
-- before writing.

writeFileLn :: FilePath -> String -> IO ()
writeFileLn p buf = writeFile p (buf ++ "\n")