Commit 7a2ac1a0 authored by Michael Hanus's avatar Michael Hanus
Browse files

Updates for new test suite spec

parent 73471a0c
{
"name": "cpm",
"version": "0.0.1",
"version": "0.0.2",
"author": "Jonas Oberschweiber, Michael Hanus",
"maintainer": "Michael Hanus <mh@informatik.uni-kiel.de>",
"synopsis": "Curry Package Manager: a tool to distribute and install Curry libraries and applications",
......
......@@ -6,11 +6,11 @@
module CPM.Config
( Config ( Config, packageInstallDir, binInstallDir, repositoryDir
, packageIndexRepository )
, readConfiguration, defaultConfig) where
, binPackageDir, packageIndexRepository )
, readConfiguration, readConfigurationWithDefault, defaultConfig ) where
import Char (isSpace)
import Directory (doesFileExist, getHomeDirectory, createDirectoryIfMissing)
import Directory (getHomeDirectory, createDirectoryIfMissing)
import FilePath ((</>))
import Function ((***))
import List (splitOn, intersperse)
......@@ -18,6 +18,7 @@ import Maybe (mapMaybe)
import PropertyFile (readPropertyFile)
import CPM.ErrorLogger
import CPM.FileUtil (ifFileExists)
--- The location of the central package index.
packageIndexURI :: String
......@@ -34,6 +35,8 @@ data Config = Config {
, binInstallDir :: String
--- Directory where the package repository is stored
, repositoryDir :: String
--- Directory where the packages with binary installation only are stored
, binPackageDir :: String
--- URL to the package index repository
, packageIndexRepository :: String
}
......@@ -42,24 +45,33 @@ data Config = Config {
--- or a new value for the option is not specified in the .cpmrc file.
defaultConfig :: Config
defaultConfig = Config
{ packageInstallDir = "$HOME/.cpm/packages"
, binInstallDir = "$HOME/.cpm/bin"
, repositoryDir = "$HOME/.cpm/index"
{ packageInstallDir = "$HOME/.cpm/packages"
, binInstallDir = "$HOME/.cpm/bin"
, repositoryDir = "$HOME/.cpm/index"
, binPackageDir = "$HOME/.cpm/bin_packages"
, packageIndexRepository = packageIndexURI }
--- Reads the .cpmrc file from the user's home directory (if present) and merges
--- its contents into the default configuration. Resolves the $HOME variable
--- after merging and creates any missing directories. May return an error using
--- Left.
--- after merging and creates any missing directories.
--- May return an error using Left.
readConfiguration :: IO (Either String Config)
readConfiguration = do
readConfiguration = readConfigurationWithDefault []
--- Reads the .cpmrc file from the user's home directory (if present) and merges
--- its contents and some given default settings into the default configuration.
--- Resolves the $HOME variable after merging and creates
--- any missing directories. May return an error using Left.
readConfigurationWithDefault :: [(String,String)] -> IO (Either String Config)
readConfigurationWithDefault defsettings = do
home <- getHomeDirectory
configFile <- return $ home </> ".cpmrc"
exists <- doesFileExist configFile
settingsFromFile <- if exists
then readPropertyFile configFile >>= \p -> return $ stripProps p
else return []
mergedSettings <- return $ mergeConfigFile defaultConfig settingsFromFile
settingsFromFile <-
ifFileExists configFile
(readPropertyFile configFile >>= \p -> return $ stripProps p)
(return [])
let mergedSettings = mergeConfigSettings defaultConfig
(settingsFromFile ++ stripProps defsettings)
case mergedSettings of
Left e -> return $ Left e
Right s' -> replaceHome s' >>= \s'' -> createDirectories s'' >>
......@@ -72,6 +84,7 @@ replaceHome cfg = do
packageInstallDir = replaceHome' homeDir (packageInstallDir cfg)
, binInstallDir = replaceHome' homeDir (binInstallDir cfg)
, repositoryDir = replaceHome' homeDir (repositoryDir cfg)
, binPackageDir = replaceHome' homeDir (binPackageDir cfg)
}
where
replaceHome' h s = concat $ intersperse h $ splitOn "$HOME" s
......@@ -81,19 +94,22 @@ createDirectories cfg = do
createDirectoryIfMissing True (packageInstallDir cfg)
createDirectoryIfMissing True (binInstallDir cfg)
createDirectoryIfMissing True (repositoryDir cfg)
createDirectoryIfMissing True (binPackageDir cfg)
--- Merges configuration options from a configuration file into a configuration
--- record. May return an error using Left.
--- Merges configuration options from a configuration file or argument options
--- into a configuration record. May return an error using Left.
---
--- @param cfg - the configuration record to merge into
--- @param opts - the options to merge
mergeConfigFile :: Config -> [(String, String)] -> Either String Config
mergeConfigFile cfg props = applyEither setters cfg
mergeConfigSettings :: Config -> [(String, String)] -> Either String Config
mergeConfigSettings cfg props = applyEither setters cfg
where
setters = mapMaybe id $ map maybeApply props
setters = map maybeApply props
maybeApply (k, v) = case lookup k keySetters of
Nothing -> Nothing
Just s -> Just $ s v
Nothing -> \_ -> Left $ "Unknown .cpmrc property: " ++ k ++ "\n\n" ++
"The following .cpmrc properties are allowed:\n" ++
unlines (map fst keySetters)
Just s -> \c -> Right $ s v c
--- Removes leading and trailing whitespace from option keys and values.
---
......@@ -105,11 +121,12 @@ stripProps = map (strip *** strip)
--- A map from option names to functions that will update a configuration
--- record with a value for that option.
keySetters :: [(String, String -> Config -> Either String Config)]
keySetters :: [(String, String -> Config -> Config)]
keySetters =
[ ("repository_path" , \v c -> Right $ c { repositoryDir = v })
, ("package_install_path", \v c -> Right $ c { packageInstallDir = v})
, ("bin_install_path" , \v c -> Right $ c { binInstallDir = v})
[ ("repository_path" , \v c -> c { repositoryDir = v })
, ("package_install_path", \v c -> c { packageInstallDir = v})
, ("bin_install_path" , \v c -> c { binInstallDir = v})
, ("bin_package_path" , \v c -> c { binPackageDir = v})
]
--- Sequentially applies a list of functions that transform a value to a value
......
......@@ -18,6 +18,7 @@ module CPM.ErrorLogger
, failIO
, log
, showLogEntry
, infoMessage, debugMessage
) where
import Global
......@@ -174,4 +175,12 @@ log lvl msg =
else
return $ ([LogEntry lvl msg], Right ())
where
showTime t = show (t `div` 1000) ++ "." ++ show ((t `mod` 1000) `div` 10)
\ No newline at end of file
showTime t = show (t `div` 1000) ++ "." ++ show ((t `mod` 1000) `div` 10)
--- Prints an info message in the standard IO monad.
infoMessage :: String -> IO ()
infoMessage msg = (log Info msg |> succeedIO ()) >> done
--- Prints a debug message in the standard IO monad.
debugMessage :: String -> IO ()
debugMessage msg = (log Debug msg |> succeedIO ()) >> done
......@@ -17,15 +17,18 @@ module CPM.FileUtil
, inDirectory
, recreateDirectory
, removeDirectoryComplete
, safeReadFile, checkAndGetDirectoryContents
, whenFileExists, ifFileExists
) where
import Directory ( doesFileExist, getCurrentDirectory, setCurrentDirectory
import Directory ( doesFileExist, doesDirectoryExist, getCurrentDirectory
, setCurrentDirectory, getDirectoryContents
, getTemporaryDirectory, doesDirectoryExist, createDirectory
, createDirectoryIfMissing)
import System (system, getEnviron)
import IOExts (evalCmd)
import FilePath (FilePath, replaceFileName, (</>), searchPathSeparator)
import List (intercalate, splitOn)
import System ( system, getEnviron, exitWith )
import IOExts ( evalCmd, readCompleteFile )
import FilePath ( FilePath, replaceFileName, (</>), searchPathSeparator )
import List ( intercalate, splitOn )
--- Joins a list of directories into a search path.
joinSearchPath :: [FilePath] -> String
......@@ -90,12 +93,12 @@ tempDir = do
--- directory.
inTempDir :: IO b -> IO b
inTempDir b = do
t <- getTemporaryDirectory
exists <- doesDirectoryExist (t </> "cpm")
t <- tempDir
exists <- doesDirectoryExist t
if exists
then return ()
else createDirectory (t </> "cpm")
inDirectory (t </> "cpm") b
else createDirectory t
inDirectory t b
--- Executes an IO action with the current directory set to a specific
--- directory.
......@@ -119,3 +122,31 @@ removeDirectoryComplete :: String -> IO ()
removeDirectoryComplete dir = do
exists <- doesDirectoryExist dir
when exists $ system ("rm -Rf " ++ quote dir) >> done
--- Reads the complete contents of a file and catches any error
--- (which is returned).
safeReadFile :: String -> IO (Either IOError String)
safeReadFile fname = do
catch (readCompleteFile fname >>= return . Right)
(return . Left)
--- Returns the list of all entries in a directory and terminates with
--- an error message if the directory does not exist.
checkAndGetDirectoryContents :: FilePath -> IO [FilePath]
checkAndGetDirectoryContents dir = do
exdir <- doesDirectoryExist dir
if exdir then getDirectoryContents dir
else do putStrLn $ "ERROR: Directory '" ++ dir ++ "' does not exist!"
exitWith 1
--- Performs an action when a file exists.
whenFileExists :: FilePath -> IO () -> IO ()
whenFileExists fname act = do
exfile <- doesFileExist fname
when exfile act
--- Performs one of two actions depending on the existence of a file.
ifFileExists :: FilePath -> IO a -> IO a -> IO a
ifFileExists fname thenact elseact = do
exfile <- doesFileExist fname
if exfile then thenact else elseact
......@@ -39,7 +39,6 @@ module CPM.Package
) where
import Char
import Directory (doesFileExist)
import List (intercalate, isInfixOf)
import FilePath ((</>))
import SetFunctions
......@@ -52,6 +51,7 @@ import DetParse
import Read (readInt)
import CPM.ErrorLogger
import CPM.FileUtil (ifFileExists)
--- A Version. Tuple components are major, minor, patch, prerelease, e.g.
--- 3.1.1-rc5
......@@ -95,9 +95,10 @@ data PackageId = PackageId String Version
data PackageExecutable = PackageExecutable String String
--- The specification of a test suite for a package.
--- It consists of a list of directories to be included in the
--- load path and a list of modules to be tested.
data PackageTests = PackageTests [String] [String]
--- It consists of a list of directory/modules pairs.
--- Each pair specifies a test which is performed in the given directoy
--- by running CurryCheck on the given list of modules.
data PackageTests = PackageTests [(String,[String])]
--- A source where the contents of a package can be acquired.
--- @cons Http - URL to a ZIP file
......@@ -136,6 +137,7 @@ data Package = Package {
, compilerCompatibility :: [CompilerCompatibility]
, source :: Maybe PackageSource
, exportedModules :: [String]
, configModule :: Maybe String
, executableSpec :: Maybe PackageExecutable
, testSuite :: Maybe PackageTests
}
......@@ -160,6 +162,7 @@ emptyPackage = Package {
, compilerCompatibility = []
, source = Nothing
, exportedModules = []
, configModule = Nothing
, executableSpec = Nothing
, testSuite = Nothing
}
......@@ -191,15 +194,14 @@ writePackageSpec pkg file = writeFile file $ ppJSON $ packageSpecToJSON pkg
--- @param the directory containing the package.json file
loadPackageSpec :: String -> IO (ErrorLogger Package)
loadPackageSpec dir = do
exists <- doesFileExist packageFile
if exists
then do
contents <- readFile packageFile
case readPackageSpec contents of
Left err -> failIO err
Right v -> succeedIO v
else failIO $ "Illegal package: file `package.json' does not exist!"
where packageFile = dir </> "package.json"
let packageFile = dir </> "package.json"
ifFileExists packageFile
(do debugMessage $ "Reading package specification '" ++ packageFile ++ "'..."
contents <- readFile packageFile
case readPackageSpec contents of
Left err -> failIO err
Right v -> succeedIO v )
(failIO $ "Illegal package: file `package.json' does not exist!")
--- Checks whether two package ids are equal, i.e. if their names and versions
--- match.
......@@ -312,7 +314,7 @@ showVersionConstraint (VCompatible v) = " ~> " ++ (showVersion v)
--- Renders the id of a package as a string. Package name and version separated
--- by a dash.
packageId :: Package -> String
packageId p = (name p) ++ "-" ++ (showVersion $ version p)
packageId p = name p ++ "-" ++ showVersion (version p)
--- Reads a package spec from a JSON string.
readPackageSpec :: String -> Either String Package
......@@ -324,19 +326,21 @@ readPackageSpec s = case parseJSON s of
--- Reads a package spec from the key-value-pairs of a JObject.
packageSpecFromJObject :: [(String, JValue)] -> Either String Package
packageSpecFromJObject kv = mandatoryString "name" $ \name ->
mandatoryString "version" $ \versionS ->
mandatoryString "author" $ \author ->
optionalString "maintainer" $ \maintainer ->
mandatoryString "synopsis" $ \synopsis ->
optionalString "description" $ \description ->
packageSpecFromJObject kv =
mandatoryString "name" kv $ \name ->
mandatoryString "version" kv $ \versionS ->
mandatoryString "author" kv $ \author ->
optionalString "maintainer" kv $ \maintainer ->
mandatoryString "synopsis" kv $ \synopsis ->
optionalString "description" kv $ \description ->
getStringList "A category" "category" $ \categories ->
optionalString "license" $ \license ->
optionalString "licenseFile" $ \licenseFile ->
optionalString "copyright" $ \copyright ->
optionalString "homepage" $ \homepage ->
optionalString "bugReports" $ \bugReports ->
optionalString "repository" $ \repository ->
optionalString "license" kv $ \license ->
optionalString "licenseFile" kv $ \licenseFile ->
optionalString "copyright" kv $ \copyright ->
optionalString "homepage" kv $ \homepage ->
optionalString "bugReports" kv $ \bugReports ->
optionalString "repository" kv $ \repository ->
optionalString "configModule" kv $ \configModule ->
mustBeVersion versionS $ \version ->
getDependencies $ \dependencies ->
getSource $ \source ->
......@@ -362,35 +366,11 @@ packageSpecFromJObject kv = mandatoryString "name" $ \name ->
, compilerCompatibility = compilerCompatibility
, source = source
, exportedModules = exportedModules
, configModule = configModule
, executableSpec = executable
, testSuite = testsuite
}
where
mandatoryString :: String -> (String -> Either String a) -> Either String a
mandatoryString k f = case lookup k kv of
Nothing -> Left $ "Mandatory field missing: '" ++ k ++ "'"
Just (JString s) -> f s
Just (JObject _) -> Left $ "Expected a string, got an object" ++ forKey
Just (JArray _) -> Left $ "Expected a string, got an array" ++ forKey
Just (JNumber _) -> Left $ "Expected a string, got a number" ++ forKey
Just JTrue -> Left $ "Expected a string, got 'true'" ++ forKey
Just JFalse -> Left $ "Expected a string, got 'false'" ++ forKey
Just JNull -> Left $ "Expected a string, got 'null'" ++ forKey
where forKey = " for key '" ++ k ++ "'"
optionalString :: String -> (Maybe String -> Either String a)
-> Either String a
optionalString k f = case lookup k kv of
Nothing -> f Nothing
Just (JString s) -> f (Just s)
Just (JObject _) -> Left $ "Expected a string, got an object" ++ forKey
Just (JArray _) -> Left $ "Expected a string, got an array" ++ forKey
Just (JNumber _) -> Left $ "Expected a string, got a number" ++ forKey
Just JTrue -> Left $ "Expected a string, got 'true'" ++ forKey
Just JFalse -> Left $ "Expected a string, got 'false'" ++ forKey
Just JNull -> Left $ "Expected a string, got 'null'" ++ forKey
where forKey = " for key '" ++ k ++ "'"
mustBeVersion :: String -> (Version -> Either String a) -> Either String a
mustBeVersion s f = case readVersion s of
Nothing -> Left $ "'" ++ s ++ "' is not a valid version specification."
......@@ -410,7 +390,8 @@ packageSpecFromJObject kv = mandatoryString "name" $ \name ->
Just JNull -> Left $ "Expected an object, got 'null'" ++ forKey
where forKey = " for key 'dependencies'"
getCompilerCompatibility :: ([CompilerCompatibility] -> Either String a) -> Either String a
getCompilerCompatibility :: ([CompilerCompatibility] -> Either String a)
-> Either String a
getCompilerCompatibility f = case lookup "compilerCompatibility" kv of
Nothing -> f []
Just (JObject ds) -> case compilerCompatibilityFromJObject ds of
......@@ -469,17 +450,46 @@ packageSpecFromJObject kv = mandatoryString "name" $ \name ->
getTestSuite :: (Maybe PackageTests -> Either String a) -> Either String a
getTestSuite f = case lookup "testsuite" kv of
Nothing -> f Nothing
Nothing -> f Nothing
Just (JObject s) -> case testSuiteFromJObject s of Left e -> Left e
Right s' -> f (Just s')
Just (JArray a) -> case testSuiteFromJArray a of
Left e -> Left e
Right s' -> f (Just s')
Just (JString _) -> Left $ "Expected an object, got a string" ++ forKey
Just (JArray _) -> Left $ "Expected an object, got an array" ++ forKey
Just (JNumber _) -> Left $ "Expected an object, got a number" ++ forKey
Just JTrue -> Left $ "Expected an object, got 'true'" ++ forKey
Just JFalse -> Left $ "Expected an object, got 'false'" ++ forKey
Just JNull -> Left $ "Expected an object, got 'null'" ++ forKey
where forKey = " for key 'testsuite'"
mandatoryString :: String -> [(String, JValue)]
-> (String -> Either String a) -> Either String a
mandatoryString k kv f = case lookup k kv of
Nothing -> Left $ "Mandatory field missing: '" ++ k ++ "'"
Just (JString s) -> f s
Just (JObject _) -> Left $ "Expected a string, got an object" ++ forKey
Just (JArray _) -> Left $ "Expected a string, got an array" ++ forKey
Just (JNumber _) -> Left $ "Expected a string, got a number" ++ forKey
Just JTrue -> Left $ "Expected a string, got 'true'" ++ forKey
Just JFalse -> Left $ "Expected a string, got 'false'" ++ forKey
Just JNull -> Left $ "Expected a string, got 'null'" ++ forKey
where forKey = " for key '" ++ k ++ "'"
optionalString :: String -> [(String, JValue)]
-> (Maybe String -> Either String a) -> Either String a
optionalString k kv f = case lookup k kv of
Nothing -> f Nothing
Just (JString s) -> f (Just s)
Just (JObject _) -> Left $ "Expected a string, got an object" ++ forKey
Just (JArray _) -> Left $ "Expected a string, got an array" ++ forKey
Just (JNumber _) -> Left $ "Expected a string, got a number" ++ forKey
Just JTrue -> Left $ "Expected a string, got 'true'" ++ forKey
Just JFalse -> Left $ "Expected a string, got 'false'" ++ forKey
Just JNull -> Left $ "Expected a string, got 'null'" ++ forKey
where forKey = " for key '" ++ k ++ "'"
test_specFromJObject_mandatoryFields :: Test.EasyCheck.Prop
test_specFromJObject_mandatoryFields =
is (packageSpecFromJObject obj)
......@@ -501,11 +511,12 @@ test_specFromJObject_minimalSpec =
test x = author p == "me" && name p == "mypackage"
where p = (head . rights) [x]
--- Reads the list of exported modules from a list of JValues.
--- Reads a list of strings from a list of JValues.
stringsFromJArray :: String -> [JValue] -> Either String [String]
stringsFromJArray ekind a = if any isLeft strings
then Left $ head $ lefts strings
else Right $ rights strings
stringsFromJArray ekind a =
if any isLeft strings
then Left $ head $ lefts strings
else Right $ rights strings
where
strings = map extractString a
extractString s = case s of
......@@ -518,17 +529,20 @@ dependenciesFromJObject :: [(String, JValue)] -> Either String [Dependency]
dependenciesFromJObject kv = if any isLeft dependencies
then Left $ intercalate "; " (lefts dependencies)
else Right $ rights dependencies
where
dependencies = map buildDependency kv
buildDependency (pkg, JString vc) = case readVersionConstraints vc of
Nothing -> Left $ "Invalid constraint '" ++ vc ++ "' for package '" ++ pkg ++ "'"
Just v -> Right $ Dependency pkg v
buildDependency (_, JObject _) = Left "Version constraint must be a string"
buildDependency (_, JArray _) = Left "Version constraint must be a string"
buildDependency (_, JNumber _) = Left "Version constraint must be a string"
buildDependency (_, JTrue ) = Left "Version constraint must be a string"
buildDependency (_, JFalse ) = Left "Version constraint must be a string"
buildDependency (_, JNull ) = Left "Version constraint must be a string"
where
dependencies = map buildDependency kv
buildDependency (pkg, JString vc) = case readVersionConstraints vc of
Nothing -> Left $ "Invalid constraint '" ++ vc ++ "' for package '" ++
pkg ++ "'"
Just v -> Right $ Dependency pkg v
buildDependency (_, JObject _) = wrongVersionConstraint
buildDependency (_, JArray _) = wrongVersionConstraint
buildDependency (_, JNumber _) = wrongVersionConstraint
buildDependency (_, JTrue ) = wrongVersionConstraint
buildDependency (_, JFalse ) = wrongVersionConstraint
buildDependency (_, JNull ) = wrongVersionConstraint
wrongVersionConstraint = Left "Version constraint must be a string"
--- Reads the compiler compatibility constraints of a package from the
--- key-value-pairs of a JObject.
......@@ -539,14 +553,17 @@ compilerCompatibilityFromJObject kv = if any isLeft compilerCompats
where
compilerCompats = map buildCompilerCompat kv
buildCompilerCompat (c, JString vc) = case readVersionConstraints vc of
Nothing -> Left $ "Invalid constraint '" ++ vc ++ "' for compiler '" ++ c ++ "'"
Nothing -> Left $ "Invalid constraint '" ++ vc ++ "' for compiler '" ++
c ++ "'"
Just v -> Right $ CompilerCompatibility c v
buildCompilerCompat (_, JObject _) = Left "Version constraint must be a string"
buildCompilerCompat (_, JArray _) = Left "Version constraint must be a string"
buildCompilerCompat (_, JNumber _) = Left "Version constraint must be a string"
buildCompilerCompat (_, JTrue ) = Left "Version constraint must be a string"
buildCompilerCompat (_, JFalse ) = Left "Version constraint must be a string"
buildCompilerCompat (_, JNull ) = Left "Version constraint must be a string"
buildCompilerCompat (_, JObject _) = wrongVersionConstraint
buildCompilerCompat (_, JArray _) = wrongVersionConstraint
buildCompilerCompat (_, JNumber _) = wrongVersionConstraint
buildCompilerCompat (_, JTrue ) = wrongVersionConstraint
buildCompilerCompat (_, JFalse ) = wrongVersionConstraint
buildCompilerCompat (_, JNull ) = wrongVersionConstraint
wrongVersionConstraint = Left "Version constraint must be a string"
--- Read source specification from the key-value-pairs of a JObject.
sourceFromJObject :: [(String, JValue)] -> Either String PackageSource
......@@ -572,23 +589,33 @@ revisionFromJObject kv = case lookup "tag" kv of
else Right $ Just $ Tag tag
Just _ -> Left "Tag expects string"
--- Read executable specification from the key-value-pairs of a JObject.
--- Reads executable specification from the key-value-pairs of a JObject.
execSpecFromJObject :: [(String, JValue)] -> Either String PackageExecutable
execSpecFromJObject kv = case lookup "name" kv of
Nothing -> Left $ "Name of executable not provided"
Just (JString name) -> case lookup "main" kv of
Nothing -> Right $ PackageExecutable name "Main"
Just (JString main) -> Right $ PackageExecutable name main
Just _ -> Left $ "Main module of executable must be a string"
Just _ -> Left "Name of executable must be a string"
--- Read a test suite specification from the key-value-pairs of a JObject.
execSpecFromJObject kv =
mandatoryString "name" kv $ \name ->
optionalString "main" kv $ \main ->
Right $ PackageExecutable name (maybe "" id main)
--- Reads a test suite specification from the key-value-pairs of a JObject.
testSuiteFromJObject :: [(String, JValue)] -> Either String PackageTests
testSuiteFromJObject kv = case getOptStringList True "src-dir" kv of
Left e -> Left e
Right dirs -> case getOptStringList False "module" kv of
Left e -> Left e
Right mods -> Right (PackageTests dirs mods)
testSuiteFromJObject kv =
mandatoryString "src-dir" kv $ \dir ->
case getOptStringList False "module" kv of
Left e -> Left e
Right mods -> Right (PackageTests [(dir,mods)])
--- Reads the list of testsuites from a list of JValues (testsuite objects).
testSuiteFromJArray :: [JValue] -> Either String PackageTests
testSuiteFromJArray a =
if any isLeft tests
then Left $ head $ lefts tests
else Right $ PackageTests (concatMap (\ (PackageTests t) -> t)
(rights tests))
where
tests = map extractTests a
extractTests s = case s of
JObject o -> testSuiteFromJObject o
_ -> Left "Array element must be a testsuite object"
--- Reads an (optional) key with a string list value.
getOptStringList :: Bool -> String -> [(String, JValue)]
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment