packages feed

hs-tree-sitter-capi-13.0.14.0: test/Test/TreeSitter/CApi.hs

{-|
Tests for `TreeSitter.CApi`.
-}
module Test.TreeSitter.CApi where

import Control.Exception (bracket)
import Control.Monad (when)
import Data.Foldable (for_)
import Foreign (castPtr, nullPtr)
import Foreign.C (withCStringLen)
import Foreign.C.ConstPtr.Compat (ConstPtr (..))
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (testCase, (@=?))
import TreeSitter.CApi (
  TSStateId (..),
  ts_language_delete,
  ts_language_state_count,
  ts_lookahead_iterator_current_symbol,
  ts_lookahead_iterator_delete,
  ts_lookahead_iterator_new,
  ts_lookahead_iterator_next,
  ts_parser_delete,
  ts_parser_new,
  ts_parser_parse_string,
 )
import TreeSitter.While (tree_sitter_while)

-- | All tests for `TreeSitter.CApi`.
tests :: TestTree
tests =
  testGroup
    "CApi"
    [ test_ts_lookaheaditerator
    , test_ts_language_delete
    , test_ts_parser_parse_fails_without_language
    ]

-- | Does the lookahead iterator work?
test_ts_lookaheaditerator :: TestTree
test_ts_lookaheaditerator =
  testCase "test_ts_lookaheaditerator" $ do
    language <- castConstPtr <$> tree_sitter_while
    stateCount <- ts_language_state_count language
    for_ [0 .. stateCount - 1] $ \stateId -> do
      lookaheadIterator <- ts_lookahead_iterator_new language (TSStateId . fromIntegral $ stateId)
      let loop = do
            _currentSymbol <- ts_lookahead_iterator_current_symbol (ConstPtr lookaheadIterator)
            success <- ts_lookahead_iterator_next lookaheadIterator
            when (success >= 1) loop
      loop
      ts_lookahead_iterator_delete lookaheadIterator

-- | Is deleting a @`TreeSitter.CApi.TSLanguage`@ multiple times safe?
test_ts_language_delete :: TestTree
test_ts_language_delete =
  testCase "ts_language_delete" $ do
    language <- castConstPtr <$> tree_sitter_while
    ts_language_delete language
    ts_language_delete language

-- | Does @`ts_parser_parse_string`@ return @NULL@ when the parser does not have a language assigned.
test_ts_parser_parse_fails_without_language :: TestTree
test_ts_parser_parse_fails_without_language =
  testCase "test_ts_parser_parse fails without language" $ do
    bracket ts_parser_new ts_parser_delete $ \ts_parser ->
      withCStringLen "" $ \(string, string_length) -> do
        tree_p <-
          ts_parser_parse_string
            ts_parser
            (ConstPtr nullPtr)
            (ConstPtr string)
            (fromIntegral string_length)
        nullPtr @=? tree_p

-- | Internal helper: Cast a t`ConstPtr` using `castPtr`.
castConstPtr :: ConstPtr a -> ConstPtr b
castConstPtr = ConstPtr . castPtr . unConstPtr