content: {-# LANGUAGE OverloadedStrings #-}
  
  module Samples where
  
  import           Test.Tasty (TestTree, testGroup)
  import           Test.Tasty.Golden (goldenVsFile)
  import           System.FilePath ((</>))
  import           Data.Maybe (mapMaybe)
  
  import qualified Data.List
  import qualified Data.Text
  import qualified Data.Text.IO
  import qualified System.Environment
  import qualified System.FilePath
  import qualified System.Directory
  import qualified System.IO
  import qualified System.IO.Temp
  import qualified System.Process
  
  import           Data.Text.IO                   ( hPutStrLn )
  import qualified Update.Nix.FetchGit
  import           Update.Nix.FetchGit.Types      ( Dryness(..)
                                                  , Env(Env)
                                                  )
  
  -- | Provided output file @f@ pointing to e.g. @tests/test_rec_sets.out.nix@
  -- * turn this into @tests/test_rec_sets.in.nix@
  -- * copy to temporary directory
  -- * create fake git repositories using @tests/fakeRepo.sh@
  -- * adjust @url@ to point to the faked git repositories in the temporary directory
  -- * perform update
  -- * adjust @url@ to point to the expected one
  -- * copy file back to @tests/test_rec_sets.out.nix@ so it be compared to @expected.nix@
  runTest :: String -> IO ()
  runTest f =
    System.IO.Temp.withSystemTempDirectory "test-update-nix-fetchgit" $ \dir ->
      System.IO.Temp.withSystemTempDirectory "test-update-nix-fetchgit-store" $ \storeDir -> do
    let
        inFile = Data.Text.unpack
          $ Data.Text.replace ".out.nix" ".in.nix"
          $ Data.Text.pack f
        inBase = System.FilePath.takeBaseName inFile
  
    System.Directory.copyFile inFile (dir </> inBase)
    testScript <- System.Directory.makeAbsolute "tests/fakeRepo.sh"
  
    _ <- System.Process.waitForProcess =<< System.Process.runProcess
      "bash"
      [testScript] (Just dir)
      Nothing
      Nothing Nothing Nothing
  
    replaceFile (dir </> inBase) "/tmp/nix-update-fetchgit-test" (Data.Text.pack dir)
  
    System.Environment.setEnv "NIX_STATE_DIR" $ storeDir </> "state"
    System.Environment.setEnv "NIX_STORE_DIR" storeDir
  
    -- work around race condition https://github.com/NixOS/nix/issues/2706
    System.Directory.createDirectoryIfMissing True $ storeDir </> "state/gcroots"
  
    -- and another - error: SQLite database storeDir </> 'state/db/db.sqlite' is busy
    _ <- System.Process.readCreateProcess
          (System.Process.shell "nix-store --init")
          mempty
  
    let env = Env (const (Data.Text.IO.hPutStrLn System.IO.stderr)) [] [] Wet False
    Update.Nix.FetchGit.processFile env (dir </> inBase)
  
    replaceFile (dir </> inBase) (Data.Text.pack dir) "/tmp/nix-update-fetchgit-test"
  
    System.Directory.copyFile (dir </> inBase) f
    where
      replaceFile f' what with =
        Data.Text.IO.readFile f' >>= Data.Text.IO.writeFile f' . Data.Text.replace what with
  
  test_derivation :: IO TestTree
  test_derivation = do
    localSamples     <- findSamples "tests"
    networkedSamples <- findSamples "tests/networked"
    samples          <- inNixBuild >>= \case
      False -> pure $ localSamples <> networkedSamples
      True  -> do
        hPutStrLn System.IO.stderr "Skipping networked tests inside Nix build"
        pure localSamples
    pure $ testGroup "golden" $ map mk samples
   where
    mk n =
      let tEx  = (n ++ ".expected.nix")
          tOut = (n ++ ".out.nix")
      in  goldenVsFile ("update of " ++ tOut) tEx tOut (runTest tOut)
  
  findSamples :: FilePath -> IO [String]
  findSamples dir =
    fmap (dir </>)
      .   mapMaybe (dropSuffix ".in.nix")
      <$> System.Directory.listDirectory dir
  
  dropSuffix :: String -> String -> Maybe String
  dropSuffix s t = if s `Data.List.isSuffixOf` t
    then Just $ take (length t - length s) t
    else Nothing
  
  -- From https://github.com/input-output-hk/ouroboros-network
  -- | Infer from environment variables whether we are running within a Nix build
  -- (and not just a nix-shell).
  inNixBuild :: IO Bool
  inNixBuild = do
    let testEnv = fmap (maybe False (not . null)) . System.Environment.lookupEnv
    haveNixBuildDir <- testEnv "NIX_BUILD_TOP"
    inNixShell      <- testEnv "IN_NIX_SHELL"
    pure (haveNixBuildDir && not inNixShell)
  