packages feed

haskell-debugger-0.13.1.0: test/haskell/Test/Integration/Evaluate.hs

-- | Evaluate request tests ported from the NodeJS testsuite.
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Test.Integration.Evaluate (evaluateTests) where

import Control.Monad.IO.Class (liftIO)
import Data.List (isInfixOf)
import Test.DAP
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.ExpectedFailure
import qualified DAP

evaluateTests :: TestTree
evaluateTests =
#ifdef mingw32_HOST_OS
  ignoreTestBecause "Needs to be fixed for Windows (#199)" $
#endif
  testGroup "DAP.Integration.Evaluate"
    [ testCase "Return structured representation for evaluated expressions (issue #116)"
        evaluateStructured
    , testCase "Imported module bindings available in evaluate context (issue #233)"
        evaluateImportedBindings
    , expectFailBecause "#299" $
        testCase "Bindings from other modules not available in evaluate context (issue #233)"
          evaluateImportedBindingsNotInOtherModule
    ]

evaluateStructured :: Assertion
evaluateStructured =
  withTestDAPServer "test/integration/T116" [] $ \test_dir server ->
    withTestDAPServerClient server $ do
      let cfg = mkLaunchConfig test_dir "T116.hs"
      hitBreakpointWith cfg 13

      resp <- evaluate "IM.delete 0 (IM.insert 0 'a' (IM.insert 1 'b' IM.empty))"
      liftIO $ assertEqual "result is IntMap" "IntMap" (DAP.evaluateResponseResult resp)
      respChildren <- fetchChildren (DAP.evaluateResponseVariablesReference resp)
      -- fixme: it'd be good to re-use the VarViews structure here, but that's specialized to Variables for now
      case filter (\v -> DAP.variableName v == "1") respChildren of
        (v1:_) -> v1 @==? "\'b\'"
        []     -> liftIO $ assertFailure $
          "No variable named 1 in evaluation result: " ++ show respChildren
      disconnect

-- | Test that bindings from imported modules are available when evaluating
-- expressions at a breakpoint (issue #233).
evaluateImportedBindings :: Assertion
evaluateImportedBindings =
  withTestDAPServer "test/integration/T233" [] $ \test_dir server ->
    withTestDAPServerClient server $ do
      let cfg = mkLaunchConfig test_dir "T233.hs"
      hitBreakpointWith cfg 15

      -- sort is imported from Data.List
      sortResp <- evaluate "show (sort xs)"
      liftIO $ assertEqual "sort xs result" "\"[1,1,2,3,4,5,6,9]\"" (DAP.evaluateResponseResult sortResp)

      -- Map is a qualified import
      mapResp <- evaluate "show (Map.lookup \"a\" m)"
      liftIO $ assertEqual "Map.lookup result" "\"Just 1\"" (DAP.evaluateResponseResult mapResp)

      disconnect

-- | Test that bindings from modules not imported at the stopped location are
-- NOT available in the evaluate context (issue #233).
evaluateImportedBindingsNotInOtherModule :: Assertion
evaluateImportedBindingsNotInOtherModule =
  withTestDAPServer "test/integration/T233" [] $ \test_dir server ->
    withTestDAPServerClient server $ do
      let cfg = mkLaunchConfig test_dir "T233.hs"

      _ <- sync $ launchWith cfg
      waitFiltering_ EventTy "initialized"
      -- T233.hs imports Data.Map.Strict as Map; Other.hs does not
      _ <- sync $ setLineBreakpoints test_dir "T233.hs" [15]
      _ <- sync $ setLineBreakpoints test_dir "Other.hs" [4]
      _ <- sync configurationDone
      _ <- assertStoppedLocation DAP.StoppedEventReasonBreakpoint 15

      -- Stopped in T233.hs which imports Data.List and Data.Map.Strict as Map
      sortResp <- evaluate "show (sort xs)"
      liftIO $ assertEqual "sort xs result" "\"[1,1,2,3,4,5,6,9]\"" (DAP.evaluateResponseResult sortResp)

      -- Resume and stop at breakpoint in Other.hs, which does not import Map
      continueThread 0
      _ <- assertStoppedLocation DAP.StoppedEventReasonBreakpoint 4

      -- Map is not imported in Other.hs; evaluating Map.fromList should fail
      mapFailResp <- evaluate "Map.fromList [(1,'a')]"
      let result = DAP.evaluateResponseResult mapFailResp
      liftIO $ assertBool
        ("expected 'not in scope' error for Map.fromList, got: " ++ show result)
        ("not in scope" `isInfixOf` show result)

      disconnect