diff --git a/semantic.cabal b/semantic.cabal index a34fe8a12..da7d84cd6 100644 --- a/semantic.cabal +++ b/semantic.cabal @@ -289,6 +289,7 @@ test-suite test , Diffing.Algorithm.RWS.Spec , Diffing.Algorithm.SES.Spec , Diffing.Interpreter.Spec + , Graphing.Calls.Spec , Integration.Spec , Matching.Go.Spec , Numeric.Spec @@ -301,6 +302,7 @@ test-suite test , SpecHelpers , Test.Hspec.LeanCheck build-depends: aeson + , algebraic-graphs , array , async , base diff --git a/test/Graphing/Calls/Spec.hs b/test/Graphing/Calls/Spec.hs new file mode 100644 index 000000000..b6e018527 --- /dev/null +++ b/test/Graphing/Calls/Spec.hs @@ -0,0 +1,47 @@ +{-# LANGUAGE PackageImports #-} + +module Graphing.Calls.Spec ( spec ) where + +import Prelude hiding (readFile) +import Prologue +import SpecHelpers hiding (readFile) + +import Algebra.Graph +import Data.List (uncons) + +import "semantic" Data.Graph (Graph (..), topologicalSort) +import Data.Graph.Vertex +import qualified Data.Language as Language +import Semantic.Config (defaultOptions) +import Semantic.Graph +import Semantic.IO + +callGraphPythonProject paths = runTaskWithOptions defaultOptions $ do + let proxy = Proxy @'Language.Python + let lang = Language.Python + blobs <- catMaybes <$> traverse readFile (flip File lang <$> paths) + package <- parsePackage pythonParser (Project (takeDirectory (maybe "/" fst (uncons paths))) blobs lang []) + modules <- topologicalSort <$> runImportGraph proxy package + runCallGraph proxy False modules package + +spec :: Spec +spec = describe "call graphing" $ do + + let needs r n = unGraph r `shouldSatisfy` hasVertex (Variable n) + + it "should work for a simple example" $ do + res <- callGraphPythonProject ["test/fixtures/python/graphing/simple/simple.py"] + res `needs` "magnus" + + it "should evaluate both sides of an if-statement" $ do + res <- callGraphPythonProject ["test/fixtures/python/graphing/conditional/conditional.py"] + res `needs` "merle" + res `needs` "taako" + + it "should continue even when a type error is encountered" $ do + res <- callGraphPythonProject ["test/fixtures/python/graphing/typeerror/typeerror.py"] + res `needs` "lup" + + it "should continue when an unbound variable is encountered" $ do + res <- callGraphPythonProject ["test/fixtures/python/graphing/unbound/unbound.py"] + res `needs` "lucretia" diff --git a/test/Spec.hs b/test/Spec.hs index 84710a79b..b19d99eb7 100644 --- a/test/Spec.hs +++ b/test/Spec.hs @@ -16,6 +16,7 @@ import qualified Data.Term.Spec import qualified Diffing.Algorithm.RWS.Spec import qualified Diffing.Algorithm.SES.Spec import qualified Diffing.Interpreter.Spec +import qualified Graphing.Calls.Spec import qualified Integration.Spec import qualified Matching.Go.Spec import qualified Numeric.Spec @@ -52,6 +53,7 @@ main = do describe "Diffing.Algorithm.RWS" Diffing.Algorithm.RWS.Spec.spec describe "Diffing.Algorithm.SES" Diffing.Algorithm.SES.Spec.spec describe "Diffing.Interpreter" Diffing.Interpreter.Spec.spec + describe "Graphing.Calls" Graphing.Calls.Spec.spec describe "Matching" Matching.Go.Spec.spec describe "Numeric" Numeric.Spec.spec describe "Rendering.TOC" Rendering.TOC.Spec.spec diff --git a/test/fixtures/python/graphing/conditional/conditional.py b/test/fixtures/python/graphing/conditional/conditional.py new file mode 100644 index 000000000..d15a3433b --- /dev/null +++ b/test/fixtures/python/graphing/conditional/conditional.py @@ -0,0 +1,8 @@ +cond = True + +if cond: + def merle(): pass + merle() +else: + def taako(): pass + taako() diff --git a/test/fixtures/python/graphing/simple/simple.py b/test/fixtures/python/graphing/simple/simple.py new file mode 100644 index 000000000..f35c3d818 --- /dev/null +++ b/test/fixtures/python/graphing/simple/simple.py @@ -0,0 +1,4 @@ +def magnus(): + return "string" + +magnus() diff --git a/test/fixtures/python/graphing/typeerror/typeerror.py b/test/fixtures/python/graphing/typeerror/typeerror.py new file mode 100644 index 000000000..4e10838b0 --- /dev/null +++ b/test/fixtures/python/graphing/typeerror/typeerror.py @@ -0,0 +1,5 @@ +var = 1 + "thing" + +def lup(): pass + +lup() diff --git a/test/fixtures/python/graphing/unbound/unbound.py b/test/fixtures/python/graphing/unbound/unbound.py new file mode 100644 index 000000000..ba02e8246 --- /dev/null +++ b/test/fixtures/python/graphing/unbound/unbound.py @@ -0,0 +1,5 @@ +var = thing + 1 + +def lucretia(): pass + +lucretia()