packages feed

nix-lang-qq-0.1.0.0: test/Spec.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

module Main (main) where

import Data.Text (Text)
import qualified Data.Text as T
import Nix.Lang.Edit
import Nix.Lang.ExactPrint (renderExactText)
import Nix.Lang.QQ (nixQQ)
import Nix.Lang.QQ.Parsed (nixParsedQQ)
import Nix.Lang.RFCPrint (formatExpr)
import qualified Nix.Lang.Types.Ps as Ps
import Nix.Lang.Types.Syn
import Nix.Lang.Utils (unLoc)
import Test.Tasty
import Test.Tasty.HUnit

pkg :: Text -> Expr
pkg name = mkSelectAttrs (mkVar "pkgs") [name]

devShellExpr :: [Text] -> Expr
devShellExpr packageNames =
  mkApp
    (mkSelectAttrs (mkVar "pkgs") ["mkShell"])
    ( mkStaticSet
        [ ("packages", mkList (pkg <$> packageNames))
        ]
    )

main :: IO ()
main = defaultMain tests

tests :: TestTree
tests =
  testGroup
    "nix-lang-qq"
    [ testCase "builds Syn expressions" $ do
        formatExpr ([nixQQ| let x = 1; in x |] :: Expr)
          @?= "let\n  x = 1;\nin\nx",
      testCase "splices antiquoted Syn expressions" $ do
        let value = mkInt 233
        formatExpr ([nixQQ| { x = %%(value); } |] :: Expr)
          @?= "{\n  x = 233;\n}",
      testCase "converts antiquoted integers automatically" $ do
        let value = (233 :: Integer)
        formatExpr ([nixQQ| { x = %%(value); } |] :: Expr)
          @?= "{\n  x = 233;\n}",
      testCase "converts antiquoted text automatically" $ do
        let value = ("hello" :: Text)
        formatExpr ([nixQQ| %%(value) |] :: Expr)
          @?= "\"hello\"",
      testCase "converts antiquoted string automatically" $ do
        let value = ("hello" :: String)
        formatExpr ([nixQQ| %%(value) |] :: Expr)
          @?= "\"hello\"",
      testCase "converts antiquoted bool automatically" $ do
        let value = True
        formatExpr ([nixQQ| %%(value) |] :: Expr)
          @?= "true",
      testCase "supports multiple antiquotes" $ do
        formatExpr ([nixQQ| { a = %%(mkInt 1); b = %%(mkInt 2); } |] :: Expr)
          @?= "{\n  a = 1;\n  b = 2;\n}",
      testCase "keeps non-antiquote percent text untouched" $ do
        formatExpr ([nixQQ| "% %%" |] :: Expr)
          @?= "\"% %%\"",
      testCase "supports nested antiquote expressions" $ do
        formatExpr ([nixQQ| { value = %%(mkApp (mkVar "f") (mkInt 1)); } |] :: Expr)
          @?= "{\n  value = f 1;\n}",
      testCase "captures quoted closing parens inside antiquote payload" $ do
        formatExpr ([nixQQ| %%(mkText "not ) the end") |] :: Expr)
          @?= "\"not ) the end\"",
      testCase "captures escaped quotes inside antiquote payload" $ do
        formatExpr ([nixQQ| %%(mkText "a\"b") |] :: Expr)
          @?= "\"a\\\"b\"",
      testCase "antiquote works inside string interpolation" $ do
        formatExpr ([nixQQ| "a${%%(mkVar "b")}c" |] :: Expr)
          @?= "\"a${b}c\"",
      testCase "supports devShell example" $ do
        formatExpr
          [nixQQ|
            let pkgs = import <nixpkgs> {}; in
              %%(devShellExpr ["ghc", "cabal-install"])
          |]
          @?= T.intercalate
            "\n"
            [ "let",
              "  pkgs = import <nixpkgs> { };",
              "in",
              "pkgs.mkShell {",
              "  packages = [",
              "    pkgs.ghc",
              "    pkgs.cabal-install",
              "  ];",
              "}"
            ],
      testCase "rejects placeholder collision" $ do
        formatExpr
          [nixQQ|
              let __nix_lang_qq_antiquote_0 = 1; in
               [ __nix_lang_qq_antiquote_0 %%(mkInt 2) ]
             |]
          @?= "let\n  __nix_lang_qq_antiquote_0 = 1;\nin\n[\n  __nix_lang_qq_antiquote_0\n  2\n]",
      testCase "builds parsed located expressions" $ do
        renderExactText (unLoc ([nixParsedQQ| let x = 1; in x |] :: Ps.LExpr))
          @?= "let x = 1; in x",
      testCase "parsed quasiquoter works with editExpr" $ do
        let expr =
              [nixParsedQQ|
                  {
                    a = 1;
                    b = 2;
                  }
                |] ::
                Ps.LExpr
        case editExpr (bindingByKey "b" // bindingValue) (setIntLiteral 20) expr of
          Right x -> renderExactText (unLoc x) @?= "{\n  a = 1;\n  b = 20;\n}"
          Left err -> fail $ show err
    ]