Skip to content

Commit

Permalink
add some unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
byorgey committed Nov 5, 2024
1 parent 9be83f0 commit a110a32
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 11 deletions.
10 changes: 5 additions & 5 deletions src/swarm-lang/Swarm/Language/Context.hs
Original file line number Diff line number Diff line change
Expand Up @@ -282,16 +282,16 @@ buildCtxMap m (CtxTree h s) = do
CtxDelete x t s1 -> buildCtxMap (M.insert x t m) s1
CtxUnion s1 s2 -> buildCtxMap m s1 *> buildCtxMap m s2

-- | "Dessicate" a context map by replacing the actual context trees
-- | "Dehydrate" a context map by replacing the actual context trees
-- with single structure layers containing only hashes. A
-- dessicated context map is very suitable for serializing, since it
-- dehydrated context map is very suitable for serializing, since it
-- makes sharing completely explicit---even if a given context is
-- referenced multiple times, the references are simply hash values,
-- and the context is stored only once, under its hash.
dessicate :: CtxMap CtxTree t -> CtxMap (Const CtxHash) t
dessicate = M.map (restructure (\(CtxTree h1 _) -> Const h1))
dehydrate :: CtxMap CtxTree t -> CtxMap (Const CtxHash) t
dehydrate = M.map (restructure (\(CtxTree h1 _) -> Const h1))

-- | "Rehydrate" a dessicated context map by replacing every hash with
-- | "Rehydrate" a dehydrated context map by replacing every hash with
-- an actual context structure. We do this by building the result
-- as a lazy, recursive map, replacing each hash by the result we
-- get when looking it up in the map being built. A context which
Expand Down
14 changes: 8 additions & 6 deletions src/swarm-lang/Swarm/Language/JSON.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@
-- to put them all here to avoid circular module dependencies.
module Swarm.Language.JSON where

import Data.Aeson (FromJSON (..), ToJSON (..), genericParseJSON, genericToJSON, withText)
import Data.Aeson (FromJSON (..), ToJSON (..), withText)
import Data.Aeson qualified as Ae
import Swarm.Language.Pipeline (processTermEither)
import Swarm.Language.Syntax (Term)
import Swarm.Language.Syntax.Pattern (Syntax, TSyntax)
import Swarm.Language.Value (Env, Value)
import Swarm.Pretty (prettyText)
import Swarm.Util.JSON (optionsMinimize)
import Witch (into)

instance FromJSON TSyntax where
Expand All @@ -30,10 +29,13 @@ instance ToJSON Term
instance ToJSON Syntax

instance ToJSON Value where
toJSON = genericToJSON optionsMinimize
toJSON = undefined

Check warning on line 32 in src/swarm-lang/Swarm/Language/JSON.hs

View workflow job for this annotation

GitHub Actions / HLint

Warning in module Swarm.Language.JSON: Avoid restricted function ▫︎ Found: "undefined" ▫︎ Note: may break the code

instance FromJSON Value where
parseJSON = genericParseJSON optionsMinimize
parseJSON = undefined

Check warning on line 35 in src/swarm-lang/Swarm/Language/JSON.hs

View workflow job for this annotation

GitHub Actions / HLint

Warning in module Swarm.Language.JSON: Avoid restricted function ▫︎ Found: "undefined" ▫︎ Note: may break the code

deriving instance FromJSON Env
deriving instance ToJSON Env
instance ToJSON Env where
toJSON = undefined

Check warning on line 38 in src/swarm-lang/Swarm/Language/JSON.hs

View workflow job for this annotation

GitHub Actions / HLint

Warning in module Swarm.Language.JSON: Avoid restricted function ▫︎ Found: "undefined" ▫︎ Note: may break the code

instance FromJSON Env where
parseJSON = undefined

Check warning on line 41 in src/swarm-lang/Swarm/Language/JSON.hs

View workflow job for this annotation

GitHub Actions / HLint

Warning in module Swarm.Language.JSON: Avoid restricted function ▫︎ Found: "undefined" ▫︎ Note: may break the code
1 change: 1 addition & 0 deletions swarm.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,7 @@ test-suite swarm-unit
other-modules:
TestBoolExpr
TestCommand
TestContext
TestEval
TestInventory
TestLSP
Expand Down
2 changes: 2 additions & 0 deletions test/unit/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import Test.Tasty.QuickCheck (
)
import TestBoolExpr (testBoolExpr)
import TestCommand (testCommands)
import TestContext (testContext)
import TestEval (testEval)
import TestInventory (testInventory)
import TestLSP (testLSP)
Expand Down Expand Up @@ -82,6 +83,7 @@ statelessTests =
, testPrettyConst
, testBoolExpr
, testCommands
, testContext
, testHighScores
, testRepl
, testRequirements
Expand Down
63 changes: 63 additions & 0 deletions test/unit/TestContext.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
{-# LANGUAGE OverloadedStrings #-}

-- |
-- SPDX-License-Identifier: BSD-3-Clause
--
-- Swarm unit tests for contexts
module TestContext where

import Data.Map qualified as M
import Swarm.Language.Context
import Swarm.Util (showT)
import Test.Tasty (TestTree, testGroup)
import Test.Tasty.HUnit (Assertion, assertBool, assertEqual, testCase)

testContext :: TestTree
testContext =
testGroup
"Contexts"
[ testGroup
"Context equality"
[ testCase "idempotence 1" $ ctxsEqual ctx1 (ctx1 <> ctx1)
, testCase "idempotence 2" $ ctxsEqual ctx2 (ctx2 <> ctx2)
, testCase "deletion" $ ctxsEqual ctx1 (delete "z" ctx2)
, testCase "empty/delete" $ ctxsEqual empty (delete "x" ctx1)
, testCase "fromMap" $ ctxsEqual ctx2 (fromMap (M.fromList [("x", 3), ("z", 6)]))
, testCase "right bias" $ ctxsEqual ctx4 (ctx2 <> ctx3)
, testCase "commutativity" $ ctxsEqual (ctx1 <> ctx5) (ctx5 <> ctx1)
]
, testGroup
"de/rehydrate round-trip"
[ testCase "empty" $ serializeRoundTrip empty
, testCase "ctx1" $ serializeRoundTrip ctx1
, testCase "ctx2" $ serializeRoundTrip ctx2
, testCase "ctx3" $ serializeRoundTrip ctx3
, testCase "ctx4" $ serializeRoundTrip ctx4
, testCase "ctx5" $ serializeRoundTrip ctx5
, testCase "large" $ serializeRoundTrip bigCtx
, testCase "delete" $ serializeRoundTrip (delete "y" ctx4)
]
]
where
ctx1 = singleton "x" 3
ctx2 = singleton "x" 3 <> singleton "z" 6
ctx3 = singleton "x" 5 <> singleton "y" 7
ctx4 = singleton "x" 5 <> singleton "y" 7 <> singleton "z" 6
ctx5 = singleton "y" 10
bigCtx = fromMap . M.fromList $ zip (map (("x" <>) . showT) [1 :: Int ..]) [1 .. 10000]

ctxsEqual :: Ctx Int -> Ctx Int -> Assertion
ctxsEqual ctx1 ctx2 = do
-- Contexts are compared by hash for equality
assertEqual "hash equality" ctx1 ctx2

-- Make sure they are also structurally equal
assertBool "structural equality" (ctxStructEqual ctx1 ctx2)
where
ctxStructEqual (Ctx m1 _) (Ctx m2 _) = m1 == m2

serializeRoundTrip :: Ctx Int -> Assertion
serializeRoundTrip ctx = do
case getCtx (ctxHash ctx) (rehydrate (dehydrate (toCtxMap ctx))) of
Nothing -> fail "Failed to reconstitute dehydrated context"
Just ctx' -> ctxsEqual ctx ctx'

0 comments on commit a110a32

Please sign in to comment.