{-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -Wno-orphans #-} import Data.Aeson (decode) import Data.ByteString.Lazy (ByteString) import Data.List (intercalate) import Data.Maybe ( fromJust, fromMaybe, ) import Data.Monoid (Sum (..)) import Data.String (IsString (..)) import Path hiding (()) import Polysemy import Polysemy.Error import Polysemy.Operators import Polysemy.State import System.Directory (getCurrentDirectory, removeFile) import System.Environment (setEnv) import System.FilePath (()) import qualified System.XDG as XDGIO import System.XDG.Env import System.XDG.Error import System.XDG.FileSystem import System.XDG.Internal import Test.Hspec instance IsString (Path Abs Dir) where fromString = fromMaybe undefined . parseAbsDir instance IsString (Path Abs File) where fromString = fromMaybe undefined . parseAbsFile bareEnv :: EnvList bareEnv = [("HOME", "/alice")] fullEnv :: EnvList fullEnv = [ ("HOME", "/alice") , ("XDG_CONFIG_HOME", "/config") , ("XDG_DATA_HOME", "/data") , ("XDG_STATE_HOME", "/state") , ("XDG_CACHE_HOME", "/cache") , ("XDG_RUNTIME_DIR", "/runtime") ] badEnv :: EnvList badEnv = [ ("HOME", "/alice") , ("XDG_DATA_HOME", "data/") , ("XDG_DATA_DIRS", "/foo:./bar:/baz:foo/../bar") ] userFiles :: FileList Integer userFiles = [ ("/data/foo/bar", 1) , ("/config/foo/bar", 10) , ("/state/foo/bar", 100) , ("/cache/foo/bar", 200) , ("/runtime/foo/bar", 300) ] sysFiles :: FileList Integer sysFiles = [ ("/usr/local/share/foo/bar", 2) , ("/usr/share/foo/bar", 3) , ("/etc/xdg/foo/bar", 20) ] allFiles :: FileList Integer allFiles = userFiles ++ sysFiles testXDG :: EnvList -> FileList Integer -> '[Env, ReadFile Integer, WriteFile Integer, State (FileMap Integer), Error XDGError] @> a -> Either XDGError a testXDG env files = run . runError . runReadWriteFileList files . runEnvList env decodeInteger :: ByteString -> Sum Integer decodeInteger = Sum . fromMaybe 0 . decode main :: IO () main = hspec $ do describe "XDG" $ do describe "pure interpreter" $ do it "finds the data home" $ do testXDG bareEnv [] getDataHome `shouldBe` Right "/alice/.local/share" testXDG fullEnv [] getDataHome `shouldBe` Right "/data" testXDG badEnv [] getDataDirs `shouldBe` Right ["/alice/.local/share", "/foo", "/baz"] it "finds the config home" $ do testXDG bareEnv [] getConfigHome `shouldBe` Right "/alice/.config" testXDG fullEnv [] getConfigHome `shouldBe` Right "/config" it "finds the state home" $ do testXDG bareEnv [] getStateHome `shouldBe` Right "/alice/.local/state" testXDG fullEnv [] getStateHome `shouldBe` Right "/state" it "opens a data file" $ do testXDG fullEnv allFiles (readDataFile "foo/bar") `shouldBe` Right 1 testXDG fullEnv sysFiles (readDataFile "foo/bar") `shouldBe` Right 2 testXDG fullEnv allFiles (readDataFile "foo/baz") `shouldBe` Left NoReadableFile testXDG fullEnv [] (maybeRead $ readDataFile "foo/bar") `shouldBe` Right Nothing it "merges data files" $ do testXDG fullEnv allFiles (readData show "foo/bar") `shouldBe` Right "123" testXDG fullEnv [] (readData show "foo/bar") `shouldBe` Right "" it "opens a config file" $ do testXDG fullEnv userFiles (readConfigFile "foo/bar") `shouldBe` Right 10 testXDG fullEnv sysFiles (readConfigFile "foo/bar") `shouldBe` Right 20 testXDG fullEnv allFiles (readConfigFile "foo/baz") `shouldBe` Left NoReadableFile it "merges config files" $ do testXDG fullEnv allFiles (readConfig show "foo/bar") `shouldBe` Right "1020" testXDG fullEnv [] (readConfig show "foo/bar") `shouldBe` Right "" it "opens a state file" $ do testXDG fullEnv userFiles (readStateFile "foo/bar") `shouldBe` Right 100 it "opens a cache file" $ do testXDG fullEnv userFiles (readCacheFile "foo/bar") `shouldBe` Right 200 it "opens a runtime file" $ do testXDG fullEnv userFiles (readRuntimeFile "foo/bar") `shouldBe` Right 300 it "writes a config file" $ do testXDG fullEnv userFiles (writeConfigFile "foo/bar" 1000 >> readConfigFile "foo/bar") `shouldBe` Right 1000 it "writes a data file" $ do testXDG fullEnv userFiles (writeDataFile "foo/bar" 2000 >> readDataFile "foo/bar") `shouldBe` Right 2000 it "writes a cache file" $ do testXDG fullEnv userFiles (writeCacheFile "foo/bar" 3000 >> readCacheFile "foo/bar") `shouldBe` Right 3000 it "writes a state file" $ do testXDG fullEnv userFiles (writeStateFile "foo/bar" 4000 >> readStateFile "foo/bar") `shouldBe` Right 4000 it "writes a runtime file" $ do testXDG fullEnv userFiles (writeRuntimeFile "foo/bar" 5000 >> readRuntimeFile "foo/bar") `shouldBe` Right 5000 describe "IO interpreter" $ do it "opens a data file" $ do cwd <- getCurrentDirectory setEnv "XDG_DATA_HOME" $ cwd "test/dir1" setEnv "XDG_DATA_DIRS" $ intercalate ":" $ map (cwd ) ["test/dir2", "test/dir3"] (decodeInteger . fromJust) <$> XDGIO.readDataFile "foo/bar.json" `shouldReturn` 1 it "merges data files" $ do XDGIO.readData decodeInteger "foo/bar.json" `shouldReturn` 3 XDGIO.readData decodeInteger "foo/baz.json" `shouldReturn` 30 it "writes a data file" $ do XDGIO.writeDataFile "foo/quux.json" "4" (decodeInteger . fromJust) <$> XDGIO.readDataFile "foo/quux.json" `shouldReturn` 4 ( "test/dir1/foo/quux.json") <$> getCurrentDirectory >>= removeFile