Add test-case
This commit is contained in:
parent
e6b3c8d8be
commit
cb26e1727b
|
@ -34,6 +34,7 @@ dependencies:
|
||||||
- optparse-applicative
|
- optparse-applicative
|
||||||
- temporary
|
- temporary
|
||||||
- text
|
- text
|
||||||
|
- hashable
|
||||||
|
|
||||||
default-extensions:
|
default-extensions:
|
||||||
- ConstraintKinds
|
- ConstraintKinds
|
||||||
|
@ -76,7 +77,7 @@ executables:
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
test:
|
test:
|
||||||
main: Spec.hs
|
main: Main.hs
|
||||||
source-dirs: test
|
source-dirs: test
|
||||||
ghc-options:
|
ghc-options:
|
||||||
- -threaded
|
- -threaded
|
||||||
|
@ -84,3 +85,7 @@ tests:
|
||||||
- -with-rtsopts=-N
|
- -with-rtsopts=-N
|
||||||
dependencies:
|
dependencies:
|
||||||
- rubyhs
|
- rubyhs
|
||||||
|
- tasty
|
||||||
|
- tasty-hunit
|
||||||
|
- filepath
|
||||||
|
- directory
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
module Rubyhs (main) where
|
module Rubyhs (main) where
|
||||||
|
|
||||||
import Data.Foldable (traverse_)
|
import Data.Foldable (traverse_)
|
||||||
import Data.Map (Map)
|
import Data.HashMap.Strict (HashMap)
|
||||||
import Data.Set (Set)
|
import Data.Set (Set)
|
||||||
import Data.Tree (Tree(Node), Forest)
|
import Data.Tree (Tree(Node), Forest)
|
||||||
import Frelude
|
import Frelude
|
||||||
|
@ -31,7 +31,7 @@ main = do
|
||||||
Just dotPath -> drawDot dotPath x
|
Just dotPath -> drawDot dotPath x
|
||||||
traverse_ act begins
|
traverse_ act begins
|
||||||
|
|
||||||
drawDot :: FilePath -> Map Ruby.Node (Set Ruby.Node) -> IO ()
|
drawDot :: FilePath -> HashMap Ruby.Node (Set Ruby.Node) -> IO ()
|
||||||
drawDot p
|
drawDot p
|
||||||
= Text.writeFile p
|
= Text.writeFile p
|
||||||
. Graphviz.digraph
|
. Graphviz.digraph
|
||||||
|
|
|
@ -15,17 +15,21 @@ module Rubyhs.References
|
||||||
import Frelude
|
import Frelude
|
||||||
-- import Data.Language.Ruby hiding (context)
|
-- import Data.Language.Ruby hiding (context)
|
||||||
import qualified Data.Language.Ruby as Ruby
|
import qualified Data.Language.Ruby as Ruby
|
||||||
import Data.Map (Map)
|
import Data.HashMap.Strict (HashMap)
|
||||||
import qualified Data.Map as Map
|
import qualified Data.HashMap.Strict as HashMap
|
||||||
|
import qualified Data.Aeson.Encoding.Internal as Aeson
|
||||||
import Data.Kind
|
import Data.Kind
|
||||||
import Control.Monad.State
|
import Control.Monad.State
|
||||||
import qualified Data.Aeson as Aeson
|
import qualified Data.Aeson as Aeson
|
||||||
|
import qualified Data.Aeson.Types as Aeson
|
||||||
import Data.Set (Set)
|
import Data.Set (Set)
|
||||||
import qualified Data.Set as Set
|
import qualified Data.Set as Set
|
||||||
import qualified Data.Text as Text
|
import qualified Data.Text as Text
|
||||||
import Data.Coerce
|
import Data.Coerce
|
||||||
import Data.Graph (Graph, Vertex, Forest)
|
import Data.Graph (Graph, Vertex, Forest)
|
||||||
import qualified Data.Graph as Graph
|
import qualified Data.Graph as Graph
|
||||||
|
import Data.Hashable
|
||||||
|
import qualified Data.Char as Char
|
||||||
|
|
||||||
span :: G node key -> Forest node
|
span :: G node key -> Forest node
|
||||||
span (g, f, _) = fmap ((\(x, _, _) -> x) . f) <$> Graph.dff g
|
span (g, f, _) = fmap ((\(x, _, _) -> x) . f) <$> Graph.dff g
|
||||||
|
@ -49,6 +53,8 @@ instance IsList Namespace where
|
||||||
toList = coerce
|
toList = coerce
|
||||||
deriving newtype instance Semigroup Namespace
|
deriving newtype instance Semigroup Namespace
|
||||||
deriving newtype instance Monoid Namespace
|
deriving newtype instance Monoid Namespace
|
||||||
|
instance FromJSON Namespace where
|
||||||
|
parseJSON = Aeson.withText "Namespace" $ \t -> Namespace <$> (traverse pure $ Text.splitOn "::" t)
|
||||||
instance ToJSON Namespace where
|
instance ToJSON Namespace where
|
||||||
toJSON = Aeson.String . showNamespace
|
toJSON = Aeson.String . showNamespace
|
||||||
|
|
||||||
|
@ -63,14 +69,32 @@ data FQN = FQN
|
||||||
|
|
||||||
data Node = NodeDef FQN | NodeModule Namespace
|
data Node = NodeDef FQN | NodeModule Namespace
|
||||||
|
|
||||||
|
beginsWithCapital :: Text -> Bool
|
||||||
|
beginsWithCapital = Char.isUpper . Text.head
|
||||||
|
|
||||||
deriving stock instance Show Node
|
deriving stock instance Show Node
|
||||||
deriving stock instance Eq Node
|
deriving stock instance Eq Node
|
||||||
deriving stock instance Ord Node
|
deriving stock instance Ord Node
|
||||||
|
instance FromJSON Node where
|
||||||
|
parseJSON = Aeson.withText "Node" $ nodeParser
|
||||||
|
nodeParser :: Text -> Aeson.Parser Node
|
||||||
|
nodeParser = \t -> case Text.splitOn "." t of
|
||||||
|
[mods,fun] -> fmap NodeDef (FQN <$> Aeson.parseJSON (Aeson.String mods) <*> pure fun)
|
||||||
|
[mods] ->
|
||||||
|
if beginsWithCapital mods
|
||||||
|
then NodeModule <$> Aeson.parseJSON (Aeson.String mods)
|
||||||
|
else pure $ NodeDef $ FQN mempty mods
|
||||||
|
_ -> empty
|
||||||
|
instance Aeson.FromJSONKey Node where
|
||||||
|
fromJSONKey = Aeson.FromJSONKeyTextParser nodeParser
|
||||||
|
instance Aeson.ToJSONKey Node where
|
||||||
|
toJSONKey = Aeson.ToJSONKeyText prettyContext (\n -> Aeson.text $ prettyContext n)
|
||||||
instance ToJSON Node where
|
instance ToJSON Node where
|
||||||
toJSON = \case
|
toJSON = \case
|
||||||
NodeDef q -> Aeson.toJSON q
|
NodeDef q -> Aeson.toJSON q
|
||||||
NodeModule m -> Aeson.toJSON m
|
NodeModule m -> Aeson.toJSON m
|
||||||
|
instance Hashable Node where
|
||||||
|
hashWithSalt n a = hashWithSalt @Text n $ prettyContext a
|
||||||
|
|
||||||
deriving stock instance Show FQN
|
deriving stock instance Show FQN
|
||||||
deriving stock instance Eq FQN
|
deriving stock instance Eq FQN
|
||||||
|
@ -110,11 +134,13 @@ class Monad m => MyMonad (m :: Type -> Type) where
|
||||||
|
|
||||||
data Env = Env
|
data Env = Env
|
||||||
{ declarations :: Set Node
|
{ declarations :: Set Node
|
||||||
, applications :: Map Node (Set Node)
|
, applications :: HashMap Node (Set Node)
|
||||||
, context :: Context
|
, context :: Context
|
||||||
}
|
}
|
||||||
|
|
||||||
newtype Result = Result (Map Node (Set Node))
|
newtype Result = Result (HashMap Node (Set Node))
|
||||||
|
-- HACK: FromJSON and ToJSON don't agree.
|
||||||
|
deriving newtype instance FromJSON Result
|
||||||
instance ToJSON Result where
|
instance ToJSON Result where
|
||||||
toJSON (Result a) = Aeson.Object $ fromList $ go <$> toList a
|
toJSON (Result a) = Aeson.Object $ fromList $ go <$> toList a
|
||||||
where
|
where
|
||||||
|
@ -135,7 +161,7 @@ instance MyMonad (State Env) where
|
||||||
Context c <- getContext
|
Context c <- getContext
|
||||||
let
|
let
|
||||||
go env@Env{applications}
|
go env@Env{applications}
|
||||||
= env { applications = Map.insertWith mappend c (Set.singleton n) applications }
|
= env { applications = HashMap.insertWith mappend c (Set.singleton n) applications }
|
||||||
modify go
|
modify go
|
||||||
getContext = gets Rubyhs.References.context
|
getContext = gets Rubyhs.References.context
|
||||||
writeContext q = modify go
|
writeContext q = modify go
|
||||||
|
@ -153,7 +179,7 @@ class References a where
|
||||||
entries :: MyMonad m => a -> m ()
|
entries :: MyMonad m => a -> m ()
|
||||||
|
|
||||||
references :: Ruby.Begin -> Result
|
references :: Ruby.Begin -> Result
|
||||||
references q = Result $ Map.unionWith mappend applications declarations'
|
references q = Result $ HashMap.unionWith mappend applications declarations'
|
||||||
where
|
where
|
||||||
Env{declarations,applications} = execState (entries @_ @(State Env) q) emptyEnv
|
Env{declarations,applications} = execState (entries @_ @(State Env) q) emptyEnv
|
||||||
emptyEnv = Env mempty mempty (Context $ NodeModule mempty)
|
emptyEnv = Env mempty mempty (Context $ NodeModule mempty)
|
||||||
|
|
40
test/Main.hs
Normal file
40
test/Main.hs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
|
module Main (main) where
|
||||||
|
|
||||||
|
import Data.List
|
||||||
|
import Data.Ord
|
||||||
|
import Frelude
|
||||||
|
import Test.Tasty
|
||||||
|
import Test.Tasty.HUnit
|
||||||
|
import qualified Data.Language.Ruby as Ruby
|
||||||
|
import qualified Rubyhs.References as Ruby
|
||||||
|
import qualified Data.Aeson as Aeson
|
||||||
|
import System.FilePath
|
||||||
|
import Data.Map (Map)
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import Data.Set (Set)
|
||||||
|
import System.Directory (withCurrentDirectory)
|
||||||
|
import Debug.Trace
|
||||||
|
import System.IO
|
||||||
|
import qualified Data.ByteString.Lazy.Char8 as ByteString
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = withCurrentDirectory "test" $ defaultMain $ testGroup "Unit tests" tests
|
||||||
|
|
||||||
|
tests :: [TestTree]
|
||||||
|
tests = go <$> ["mod", "simple"]
|
||||||
|
where
|
||||||
|
go :: String -> TestTree
|
||||||
|
go s = testCase s $ do
|
||||||
|
let basename = "tests" </> s
|
||||||
|
begin <- Ruby.parseFile @Ruby.Begin $ basename <.> "rb"
|
||||||
|
json <- decodeFile @(HashMap Ruby.Node (Set Ruby.Node)) $ basename <.> "json"
|
||||||
|
let (Ruby.Result refs) = Ruby.references begin
|
||||||
|
when (refs /= json)
|
||||||
|
$ assertFailure
|
||||||
|
$ "Expected " <> convertString (Aeson.encode refs) <> " but got " <> convertString (Aeson.encode json)
|
||||||
|
|
||||||
|
decodeFile :: FromJSON a => FilePath -> IO a
|
||||||
|
decodeFile p = Aeson.eitherDecodeFileStrict' p >>= \case
|
||||||
|
Left err -> error err
|
||||||
|
Right a -> pure a
|
|
@ -1,2 +0,0 @@
|
||||||
main :: IO ()
|
|
||||||
main = putStrLn "Test suite not yet implemented"
|
|
7
test/tests/mod.json
Normal file
7
test/tests/mod.json
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
{
|
||||||
|
"A.b": [
|
||||||
|
"A.a"
|
||||||
|
],
|
||||||
|
"A.a": [],
|
||||||
|
"A": []
|
||||||
|
}
|
8
test/tests/mod.rb
Normal file
8
test/tests/mod.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module A
|
||||||
|
def a
|
||||||
|
end
|
||||||
|
|
||||||
|
def b
|
||||||
|
a
|
||||||
|
end
|
||||||
|
end
|
6
test/tests/simple.json
Normal file
6
test/tests/simple.json
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"a": [],
|
||||||
|
"b": [
|
||||||
|
"a"
|
||||||
|
]
|
||||||
|
}
|
6
test/tests/simple.rb
Normal file
6
test/tests/simple.rb
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
def a
|
||||||
|
end
|
||||||
|
|
||||||
|
def b
|
||||||
|
a
|
||||||
|
end
|
Loading…
Reference in a new issue