diff --git a/ARCtrl.sln b/ARCtrl.sln
index e2bba517..1e468556 100644
--- a/ARCtrl.sln
+++ b/ARCtrl.sln
@@ -88,6 +88,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ARCtrl.Yaml.Tests", "tests\
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ARCtrl.Contract.Tests", "tests\Contract\ARCtrl.Contract.Tests.fsproj", "{D10D12C7-B877-423B-867D-161D99E673C9}"
EndProject
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ARCtrl.CWL.Tests", "tests\CWL\ARCtrl.CWL.Tests.fsproj", "{0F2188D3-144C-41BF-89F6-AA85883AE0D3}"
+EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ARCtrl.ROCrate", "src\ROCrate\ARCtrl.ROCrate.fsproj", "{658BF141-B4B5-4B90-891D-AC36A3FD7574}"
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "ARCtrl.ROCrate.Tests", "tests\ROCrate\ARCtrl.ROCrate.Tests.fsproj", "{212A1C64-02FC-465A-B0FA-F69735F37ACC}"
@@ -180,6 +182,10 @@ Global
{D10D12C7-B877-423B-867D-161D99E673C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D10D12C7-B877-423B-867D-161D99E673C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D10D12C7-B877-423B-867D-161D99E673C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0F2188D3-144C-41BF-89F6-AA85883AE0D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0F2188D3-144C-41BF-89F6-AA85883AE0D3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0F2188D3-144C-41BF-89F6-AA85883AE0D3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0F2188D3-144C-41BF-89F6-AA85883AE0D3}.Release|Any CPU.Build.0 = Release|Any CPU
{658BF141-B4B5-4B90-891D-AC36A3FD7574}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{658BF141-B4B5-4B90-891D-AC36A3FD7574}.Debug|Any CPU.Build.0 = Debug|Any CPU
{658BF141-B4B5-4B90-891D-AC36A3FD7574}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -218,6 +224,7 @@ Global
{1CA11165-4B70-41D2-A846-50374E85385E} = {64B34A6E-318D-4E6E-9262-CE52C9B85A38}
{5810EF87-4F85-4B4C-98E3-833AE914C628} = {64B34A6E-318D-4E6E-9262-CE52C9B85A38}
{D10D12C7-B877-423B-867D-161D99E673C9} = {64B34A6E-318D-4E6E-9262-CE52C9B85A38}
+ {0F2188D3-144C-41BF-89F6-AA85883AE0D3} = {64B34A6E-318D-4E6E-9262-CE52C9B85A38}
{658BF141-B4B5-4B90-891D-AC36A3FD7574} = {6DA2330B-D407-4FB1-AF05-B0184034EC44}
{212A1C64-02FC-465A-B0FA-F69735F37ACC} = {64B34A6E-318D-4E6E-9262-CE52C9B85A38}
{243ACD5F-10AD-4BE6-9932-829667BE2053} = {64B34A6E-318D-4E6E-9262-CE52C9B85A38}
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 97925793..11ea1005 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -11,7 +11,7 @@
-
+
diff --git a/src/ARCtrl/ARC.fs b/src/ARCtrl/ARC.fs
index b0bebd84..4f8b4bc8 100644
--- a/src/ARCtrl/ARC.fs
+++ b/src/ARCtrl/ARC.fs
@@ -55,7 +55,7 @@ module ARCAux =
|> FileSystem.create
fs.Union(tree)
- let updateFSByCWL (cwl : CWL.CWL option) (fs : FileSystem) =
+ let updateFSByCWL (cwl : unit option) (fs : FileSystem) =
let workflows = FileSystemTree.createWorkflowsFolder [||]
let runs = FileSystemTree.createRunsFolder [||]
let tree =
@@ -65,7 +65,7 @@ module ARCAux =
[]
-type ARC(?isa : ArcInvestigation, ?cwl : CWL.CWL, ?fs : FileSystem.FileSystem) =
+type ARC(?isa : ArcInvestigation, ?cwl : unit, ?fs : FileSystem.FileSystem) =
let mutable _isa = isa
let mutable _cwl = cwl
diff --git a/src/CWL/ARCtrl.CWL.fsproj b/src/CWL/ARCtrl.CWL.fsproj
index b709cf8c..52968dfa 100644
--- a/src/CWL/ARCtrl.CWL.fsproj
+++ b/src/CWL/ARCtrl.CWL.fsproj
@@ -8,6 +8,18 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs
new file mode 100644
index 00000000..0a148161
--- /dev/null
+++ b/src/CWL/CWLTypes.fs
@@ -0,0 +1,56 @@
+namespace ARCtrl.CWL
+
+open DynamicObj
+
+type FileInstance () =
+ inherit DynamicObj ()
+
+type DirectoryInstance () =
+ inherit DynamicObj ()
+
+type DirentInstance = {
+ // can be string or expression, but expression is string as well
+ Entry: string
+ Entryname: string option
+ Writable: bool option
+}
+
+/// Primitive types with the concept of a file and directory as a builtin type.
+type CWLType =
+ /// Represents a file (or group of files when secondaryFiles is provided)
+ | File of FileInstance
+ /// Represents a directory to present to a command line tool.
+ /// Directories are represented as objects with class of Directory. Directory objects have a number of properties that provide metadata about the directory.
+ | Directory of DirectoryInstance
+ /// Define a file or subdirectory that must be placed in the designated output directory prior to executing the command line tool.
+ /// May be the result of executing an expression, such as building a configuration file from a template.
+ | Dirent of DirentInstance
+ | String
+ | Int
+ | Long
+ | Float
+ | Double
+ | Boolean
+ | Stdout
+ | Null
+ | Array of CWLType
+
+type InputRecordSchema () =
+ inherit DynamicObj ()
+
+type InputEnumSchema () =
+ inherit DynamicObj ()
+
+type InputArraySchema () =
+ inherit DynamicObj ()
+
+type SchemaDefRequirementType (types, definitions) as this =
+ inherit DynamicObj ()
+ do
+ DynObj.setProperty (nameof types) definitions this
+
+type SoftwarePackage = {
+ Package: string
+ Version: ResizeArray option
+ Specs: ResizeArray option
+}
diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs
new file mode 100644
index 00000000..9cf05f14
--- /dev/null
+++ b/src/CWL/Decode.fs
@@ -0,0 +1,569 @@
+namespace ARCtrl.CWL
+
+open YAMLicious
+open YAMLicious.YAMLiciousTypes
+open DynamicObj
+
+module ResizeArray =
+
+ let map f (a : ResizeArray<_>) =
+ let b = ResizeArray<_>()
+ for i in a do
+ b.Add(f i)
+ b
+
+module Decode =
+
+ /// Decode key value pairs into a dynamic object, while preserving their tree structure
+ let rec overflowDecoder (dynObj: DynamicObj) (dict: System.Collections.Generic.Dictionary) =
+ for e in dict do
+ match e.Value with
+ | YAMLElement.Object [YAMLElement.Value v] ->
+ DynObj.setProperty e.Key v.Value dynObj
+ | YAMLElement.Object [YAMLElement.Sequence s] ->
+ let newDynObj = new DynamicObj ()
+ (s |> List.map ((Decode.object (fun get -> (get.Overflow.FieldList []))) >> overflowDecoder newDynObj))
+ |> List.iter (fun x ->
+ DynObj.setProperty
+ e.Key
+ x
+ dynObj
+ )
+ | _ -> DynObj.setProperty e.Key e.Value dynObj
+ dynObj
+
+ /// Decode a YAMLElement which is either a string or expression into a string
+ let decodeStringOrExpression (yEle:YAMLElement) =
+ match yEle with
+ | YAMLElement.Value v | YAMLElement.Object [YAMLElement.Value v] -> v.Value
+ | YAMLElement.Object [YAMLElement.Mapping (c,YAMLElement.Object [YAMLElement.Value v])] -> sprintf "%s: %s" c.Value v.Value
+ | _ -> failwithf "%A" yEle
+
+ /// Decode a YAMLElement into a glob search pattern for output binding
+ let outputBindingGlobDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding) =
+ Decode.object (fun get ->
+ let glob = get.Optional.Field "glob" Decode.string
+ { Glob = glob }
+ )
+
+ /// Decode a YAMLElement into an OutputBinding
+ let outputBindingDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding option) =
+ Decode.object(fun get ->
+ let outputBinding = get.Optional.Field "outputBinding" outputBindingGlobDecoder
+ outputBinding
+ )
+
+ /// Decode a YAMLElement into a Dirent
+ let direntDecoder: (YAMLiciousTypes.YAMLElement -> CWLType) =
+ Decode.object (fun get ->
+ Dirent
+ {
+ // BUG: Entry Requires an Entryname to be present when it's an expression
+ Entry = get.Required.Field "entry" decodeStringOrExpression
+ Entryname = get.Optional.Field "entryname" decodeStringOrExpression
+ Writable = get.Optional.Field "writable" Decode.bool
+ }
+ )
+
+ /// Decode the contained type of a CWL Array
+ let cwlArrayTypeDecoder: (YAMLiciousTypes.YAMLElement -> CWLType) =
+ Decode.object (fun get ->
+ let items = get.Required.Field "items" Decode.string
+ match items with
+ | "File" -> Array (File (FileInstance ()))
+ | "Directory" -> Array (Directory (DirectoryInstance ()))
+ | "Dirent" -> Array (get.Required.Field "listing" direntDecoder)
+ | "string" -> Array String
+ | "int" -> Array Int
+ | "long" -> Array Long
+ | "float" -> Array Float
+ | "double" -> Array Double
+ | "boolean" -> Array Boolean
+ | _ -> failwith "Invalid CWL type"
+ )
+
+ /// Match the input string to the possible CWL types and checks if it is optional
+ let cwlTypeStringMatcher (t: string) (get: Decode.IGetters) =
+ let optional, newT =
+ if t.EndsWith("?") then
+ true, t.Replace("?", "")
+ else
+ false, t
+ match newT with
+ | "File" -> File (FileInstance ())
+ | "Directory" -> Directory (DirectoryInstance ())
+ | "Dirent" -> (get.Required.Field "listing" direntDecoder)
+ | "string" -> String
+ | "int" -> Int
+ | "long" -> Long
+ | "float" -> Float
+ | "double" -> Double
+ | "boolean" -> Boolean
+ | "File[]" -> Array (File (FileInstance ()))
+ | "Directory[]" -> Array (Directory (DirectoryInstance ()))
+ | "Dirent[]" -> Array (get.Required.Field "listing" direntDecoder)
+ | "string[]" -> Array String
+ | "int[]" -> Array Int
+ | "long[]" -> Array Long
+ | "float[]" -> Array Float
+ | "double[]" -> Array Double
+ | "boolean[]" -> Array Boolean
+ | "stdout" -> Stdout
+ | "null" -> Null
+ | _ -> failwith "Invalid CWL type"
+ , optional
+
+ /// Access the type field and decode a YAMLElement into a CWLType
+ let cwlTypeDecoder: (YAMLiciousTypes.YAMLElement -> CWLType*bool) =
+ Decode.object (fun get ->
+ let cwlType =
+ get.Required.Field
+ "type"
+ (
+ fun value ->
+ match value with
+ | YAMLElement.Value v | YAMLElement.Object [YAMLElement.Value v] -> Some v.Value
+ | YAMLElement.Object o -> None
+ | _ -> failwith "Unexpected YAMLElement"
+ )
+ match cwlType with
+ | Some t ->
+ cwlTypeStringMatcher t get
+ | None ->
+ let cwlTypeArray = get.Required.Field "type" cwlArrayTypeDecoder
+ cwlTypeArray, false
+ )
+
+ /// Decode a YAMLElement into an Output Array
+ let outputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) =
+ Decode.object (fun get ->
+ let dict = get.Overflow.FieldList []
+ [|
+ for key in dict.Keys do
+ let value = dict.[key]
+ let outputBinding = outputBindingDecoder value
+ let outputSource = get.Optional.Field "outputSource" Decode.string
+ let cwlType =
+ match value with
+ | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get |> fst
+ | _ -> cwlTypeDecoder value |> fst
+ let output =
+ CWLOutput(
+ key,
+ cwlType
+ )
+ if outputBinding.IsSome then
+ DynObj.setOptionalProperty "outputBinding" outputBinding output
+ if outputSource.IsSome then
+ DynObj.setOptionalProperty "outputSource" outputSource output
+ output
+ |]
+ |> ResizeArray
+ )
+
+ /// Access the outputs field and decode a YAMLElement into an Output Array
+ let outputsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) =
+ Decode.object (fun get ->
+ let outputs = get.Required.Field "outputs" outputArrayDecoder
+ outputs
+ )
+
+ /// Decode a YAMLElement into a DockerRequirement
+ let dockerRequirementDecoder (get: Decode.IGetters): DockerRequirement =
+ let dockerReq = {
+ DockerPull = get.Optional.Field "dockerPull" Decode.string
+ DockerFile = get.Optional.Field "dockerFile" (Decode.map id Decode.string )
+ DockerImageId = get.Optional.Field "dockerImageId" Decode.string
+ }
+ dockerReq
+
+ /// Decode a YAMLElement into an EnvVarRequirement array
+ let envVarRequirementDecoder (get: Decode.IGetters): ResizeArray =
+ let envDef =
+ get.Required.Field
+ "envDef"
+ (
+ Decode.resizearray
+ (
+ Decode.object (fun get2 ->
+ {
+ EnvName = get2.Required.Field "envName" Decode.string
+ EnvValue = get2.Required.Field "envValue" Decode.string
+ }
+ )
+ )
+ )
+ envDef
+
+ /// Decode a YAMLElement into a SoftwareRequirement array
+ let softwareRequirementDecoder (get: Decode.IGetters): ResizeArray =
+ let envDef =
+ get.Required.Field
+ "packages"
+ (
+ Decode.resizearray
+ (
+ Decode.object (fun get2 ->
+ {
+ Package = get2.Required.Field "package" Decode.string
+ Version = get2.Optional.Field "version" (Decode.resizearray Decode.string)
+ Specs = get2.Optional.Field "specs" (Decode.resizearray Decode.string)
+ }
+ )
+ )
+ )
+ envDef
+
+ /// Decode a YAMLElement into a InitialWorkDirRequirement array
+ let initialWorkDirRequirementDecoder (get: Decode.IGetters): ResizeArray =
+ let initialWorkDir =
+ //TODO: Support more than dirent
+ get.Required.Field
+ "listing"
+ (Decode.resizearray direntDecoder)
+ initialWorkDir
+
+ /// Decode a YAMLElement into a ResourceRequirementInstance
+ let resourceRequirementDecoder (get: Decode.IGetters): ResourceRequirementInstance =
+ ResourceRequirementInstance(
+ get.Optional.Field "coresMin" id,
+ get.Optional.Field "coresMax" id,
+ get.Optional.Field "ramMin" id,
+ get.Optional.Field "ramMax" id,
+ get.Optional.Field "tmpdirMin" id,
+ get.Optional.Field "tmpdirMax" id,
+ get.Optional.Field "outdirMin" id,
+ get.Optional.Field "outdirMax" id
+ )
+
+ /// Decode a YAMLElement into a SchemaDefRequirementType array
+ let schemaDefRequirementDecoder (get: Decode.IGetters): ResizeArray =
+ let schemaDef =
+ get.Required.Field
+ "types"
+ (
+ Decode.resizearray
+ (
+ Decode.map id Decode.string
+ )
+ )
+ |> ResizeArray.map (fun m -> SchemaDefRequirementType(m.Keys |> Seq.item 0, m.Values |> Seq.item 0))
+ schemaDef
+
+ /// Decode a YAMLElement into a ToolTimeLimitRequirement
+ let toolTimeLimitRequirementDecoder (get: Decode.IGetters): float =
+ get.Required.Field "timelimit" Decode.float
+
+ /// Decode all YAMLElements matching the Requirement type into a ResizeArray of Requirement
+ let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) =
+ Decode.resizearray
+ (
+ Decode.object (fun get ->
+ let cls = get.Required.Field "class" Decode.string
+ match cls with
+ | "InlineJavascriptRequirement" -> InlineJavascriptRequirement
+ | "SchemaDefRequirement" -> SchemaDefRequirement (schemaDefRequirementDecoder get)
+ | "DockerRequirement" -> DockerRequirement (dockerRequirementDecoder get)
+ | "SoftwareRequirement" -> SoftwareRequirement (softwareRequirementDecoder get)
+ | "InitialWorkDirRequirement" -> InitialWorkDirRequirement (initialWorkDirRequirementDecoder get)
+ | "EnvVarRequirement" -> EnvVarRequirement (envVarRequirementDecoder get)
+ | "ShellCommandRequirement" -> ShellCommandRequirement
+ | "ResourceRequirement" -> ResourceRequirement (resourceRequirementDecoder get)
+ | "WorkReuse" -> WorkReuseRequirement
+ | "NetworkAccess" -> NetworkAccessRequirement
+ | "InplaceUpdateRequirement" -> InplaceUpdateRequirement
+ | "ToolTimeLimit" -> ToolTimeLimitRequirement (toolTimeLimitRequirementDecoder get)
+ | "SubworkflowFeatureRequirement" -> SubworkflowFeatureRequirement
+ | "ScatterFeatureRequirement" -> ScatterFeatureRequirement
+ | "MultipleInputFeatureRequirement" -> MultipleInputFeatureRequirement
+ | "StepInputExpressionRequirement" -> StepInputExpressionRequirement
+ | _ -> failwith "Invalid requirement"
+ )
+ )
+
+ /// Access the requirements field and decode the YAMLElements into a Requirement array
+ let requirementsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray option) =
+ Decode.object (fun get ->
+ let requirements = get.Optional.Field "requirements" requirementArrayDecoder
+ requirements
+ )
+
+ /// Access the hints field and decode the YAMLElements into a Requirement array
+ let hintsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray option) =
+ Decode.object (fun get ->
+ let requirements = get.Optional.Field "hints" requirementArrayDecoder
+ requirements
+ )
+
+ /// Decode a YAMLElement into an InputBinding
+ let inputBindingDecoder: (YAMLiciousTypes.YAMLElement -> InputBinding option) =
+ Decode.object(fun get ->
+ let outputBinding =
+ get.Optional.Field
+ "inputBinding"
+ (
+ Decode.object (fun get' ->
+ {
+ Prefix = get'.Optional.Field "prefix" Decode.string
+ Position = get'.Optional.Field "position" Decode.int
+ ItemSeparator = get'.Optional.Field "itemSeparator" Decode.string
+ Separate = get'.Optional.Field "separate" Decode.bool
+ }
+ )
+ )
+ outputBinding
+ )
+
+ /// Decode a YAMLElement into an Input array
+ let inputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) =
+ Decode.object (fun get ->
+ let dict = get.Overflow.FieldList []
+ [|
+ for key in dict.Keys do
+ let value = dict.[key]
+ let inputBinding = inputBindingDecoder value
+ let cwlType,optional =
+ match value with
+ | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get
+ | _ -> cwlTypeDecoder value
+ let input =
+ CWLInput(
+ key,
+ cwlType
+ )
+ if inputBinding.IsSome then
+ DynObj.setOptionalProperty "inputBinding" inputBinding input
+ if optional then
+ DynObj.setOptionalProperty "optional" (Some true) input
+ input
+ |]
+ |> ResizeArray
+ )
+
+ /// Access the inputs field and decode the YAMLElements into an Input array
+ let inputsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray option) =
+ Decode.object (fun get ->
+ let outputs = get.Optional.Field "inputs" inputArrayDecoder
+ outputs
+ )
+
+ let baseCommandDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray option) =
+ Decode.object (fun get ->
+ get.Optional.Field "baseCommand" (Decode.resizearray Decode.string)
+ )
+
+ let versionDecoder: (YAMLiciousTypes.YAMLElement -> string) =
+ Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string)
+
+ let classDecoder: (YAMLiciousTypes.YAMLElement -> string) =
+ Decode.object (fun get ->
+ get.Required.Field "class" Decode.string
+ )
+ let stringOptionFieldDecoder field : (YAMLiciousTypes.YAMLElement -> string option) =
+ Decode.object(fun get ->
+ let fieldValue = get.Optional.Field field Decode.string
+ fieldValue
+ )
+
+ let stringFieldDecoder field : (YAMLiciousTypes.YAMLElement -> string) =
+ Decode.object(fun get ->
+ let fieldValue = get.Required.Field field Decode.string
+ fieldValue
+ )
+
+ let inputStepDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) =
+ Decode.object (fun get ->
+ let dict = get.Overflow.FieldList []
+ [|
+ for key in dict.Keys do
+ let value = dict.[key]
+ let source =
+ let s1 =
+ match value with
+ | YAMLElement.Object [YAMLElement.Value v] -> Some v.Value
+ | _ -> None
+ let s2 = stringOptionFieldDecoder "source" value
+ match s1,s2 with
+ | Some s1, _ -> Some s1
+ | _, Some s2 -> Some s2
+ | _ -> None
+ let defaultValue = stringOptionFieldDecoder "default" value
+ let valueFrom = stringOptionFieldDecoder "valueFrom" value
+ { Id = key; Source = source; DefaultValue = defaultValue; ValueFrom = valueFrom }
+ |]
+ |> ResizeArray
+ )
+
+ let outputStepsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) =
+ Decode.object (fun get ->
+ let outputs = get.Required.Field "out" (Decode.resizearray Decode.string)
+ outputs
+ )
+
+ let stepArrayDecoder =
+ Decode.object (fun get ->
+ let dict = get.Overflow.FieldList []
+ [|
+ for key in dict.Keys do
+ let value = dict.[key]
+ let run = stringFieldDecoder "run" value
+ let inputs = Decode.object (fun get -> get.Required.Field "in" inputStepDecoder) value
+ let outputs = {Id = outputStepsDecoder value}
+ let requirements = requirementsDecoder value
+ let hints = hintsDecoder value
+ let wfStep =
+ WorkflowStep(
+ key,
+ inputs,
+ outputs,
+ run
+ )
+ if requirements.IsSome then
+ wfStep.Requirements <- requirements
+ if hints.IsSome then
+ wfStep.Hints <- hints
+ wfStep
+ |]
+ |> ResizeArray
+ )
+
+ let stepsDecoder =
+ Decode.object (fun get ->
+ let steps = get.Required.Field "steps" stepArrayDecoder
+ steps
+ )
+
+ /// Decode a CWL file string written in the YAML format into a CWLToolDescription
+ let decodeCommandLineTool (cwl: string) =
+ let yamlCWL = Decode.read cwl
+ let cwlVersion = versionDecoder yamlCWL
+ let outputs = outputsDecoder yamlCWL
+ let inputs = inputsDecoder yamlCWL
+ let requirements = requirementsDecoder yamlCWL
+ let hints = hintsDecoder yamlCWL
+ let baseCommand = baseCommandDecoder yamlCWL
+ let description =
+ CWLToolDescription(
+ outputs,
+ cwlVersion
+ )
+ let metadata =
+ let md = new DynamicObj ()
+ yamlCWL
+ |> Decode.object (fun get ->
+ overflowDecoder
+ md
+ (
+ get.Overflow.FieldList [
+ "inputs";
+ "outputs";
+ "class";
+ "id";
+ "label";
+ "doc";
+ "requirements";
+ "hints";
+ "cwlVersion";
+ "baseCommand";
+ "arguments";
+ "stdin";
+ "stderr";
+ "stdout";
+ "successCodes";
+ "temporaryFailCodes";
+ "permanentFailCodes"
+ ]
+ )
+ ) |> ignore
+ md
+ yamlCWL
+ |> Decode.object (fun get ->
+ overflowDecoder
+ description
+ (
+ get.MultipleOptional.FieldList [
+ "id";
+ "label";
+ "doc";
+ "arguments";
+ "stdin";
+ "stderr";
+ "stdout";
+ "successCodes";
+ "temporaryFailCodes";
+ "permanentFailCodes"
+ ]
+ )
+ ) |> ignore
+ if inputs.IsSome then
+ description.Inputs <- inputs
+ if requirements.IsSome then
+ description.Requirements <- requirements
+ if hints.IsSome then
+ description.Hints <- hints
+ if baseCommand.IsSome then
+ description.BaseCommand <- baseCommand
+ if metadata.GetProperties(false) |> Seq.length > 0 then
+ description.Metadata <- Some metadata
+ description
+
+ /// Decode a CWL file string written in the YAML format into a CWLWorkflowDescription
+ let decodeWorkflow (cwl: string) =
+ let yamlCWL = Decode.read cwl
+ let cwlVersion = versionDecoder yamlCWL
+ let outputs = outputsDecoder yamlCWL
+ let inputs =
+ match inputsDecoder yamlCWL with
+ | Some i -> i
+ | None -> failwith "Inputs are required for a workflow"
+ let requirements = requirementsDecoder yamlCWL
+ let hints = hintsDecoder yamlCWL
+ let steps = stepsDecoder yamlCWL
+ let description =
+ CWLWorkflowDescription(
+ steps,
+ inputs,
+ outputs,
+ cwlVersion
+ )
+ let metadata =
+ let md = new DynamicObj ()
+ yamlCWL
+ |> Decode.object (fun get ->
+ overflowDecoder
+ md
+ (
+ get.Overflow.FieldList [
+ "inputs";
+ "outputs";
+ "class";
+ "steps";
+ "id";
+ "label";
+ "doc";
+ "requirements";
+ "hints";
+ "cwlVersion";
+ ]
+ )
+ ) |> ignore
+ md
+ yamlCWL
+ |> Decode.object (fun get ->
+ overflowDecoder
+ description
+ (
+ get.MultipleOptional.FieldList [
+ "id";
+ "label";
+ "doc"
+ ]
+ )
+ ) |> ignore
+ if requirements.IsSome then
+ description.Requirements <- requirements
+ if hints.IsSome then
+ description.Hints <- hints
+ if metadata.GetProperties(false) |> Seq.length > 0 then
+ description.Metadata <- Some metadata
+ description
\ No newline at end of file
diff --git a/src/CWL/Encode.fs b/src/CWL/Encode.fs
new file mode 100644
index 00000000..4e267992
--- /dev/null
+++ b/src/CWL/Encode.fs
@@ -0,0 +1 @@
+namespace ARCtrl.CWL
diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs
new file mode 100644
index 00000000..cdacb82f
--- /dev/null
+++ b/src/CWL/Inputs.fs
@@ -0,0 +1,28 @@
+namespace ARCtrl.CWL
+
+open DynamicObj
+open Fable.Core
+
+type InputBinding = {
+ Prefix: string option
+ Position: int option
+ ItemSeparator: string option
+ Separate: bool option
+}
+
+[]
+type CWLInput (
+ name: string,
+ ?type_: CWLType,
+ ?inputBinding: InputBinding,
+ ?optional: bool
+) as this =
+ inherit DynamicObj ()
+ do
+ DynObj.setOptionalProperty ("type") type_ this
+ DynObj.setOptionalProperty ("inputBinding") inputBinding this
+ DynObj.setOptionalProperty ("optional") optional this
+ member this.Name = name
+ member this.Type_ = DynObj.tryGetTypedPropertyValue ("type") this
+ member this.InputBinding = DynObj.tryGetTypedPropertyValue ("inputBinding") this
+ member this.Optional = DynObj.tryGetTypedPropertyValue ("optional") this
diff --git a/src/CWL/Library.fs b/src/CWL/Library.fs
deleted file mode 100644
index 13b78b55..00000000
--- a/src/CWL/Library.fs
+++ /dev/null
@@ -1,3 +0,0 @@
-namespace ARCtrl.CWL
-
-type CWL = unit
\ No newline at end of file
diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs
new file mode 100644
index 00000000..ae5dbf6c
--- /dev/null
+++ b/src/CWL/Outputs.fs
@@ -0,0 +1,25 @@
+namespace ARCtrl.CWL
+
+open DynamicObj
+open Fable.Core
+
+type OutputBinding = {
+ Glob: string option
+}
+
+[]
+type CWLOutput (
+ name: string,
+ ?type_: CWLType,
+ ?outputBinding: OutputBinding,
+ ?outputSource: string
+) as this =
+ inherit DynamicObj ()
+ do
+ DynObj.setOptionalProperty ("type") type_ this
+ DynObj.setOptionalProperty ("outputBinding") outputBinding this
+ DynObj.setOptionalProperty ("outputSource") outputSource this
+ member this.Name = name
+ member this.Type_ = DynObj.tryGetTypedPropertyValue ("type") this
+ member this.OutputBinding = DynObj.tryGetTypedPropertyValue ("outputBinding") this
+ member this.OutputSource = DynObj.tryGetTypedPropertyValue ("outputSource") this
\ No newline at end of file
diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs
new file mode 100644
index 00000000..fede905a
--- /dev/null
+++ b/src/CWL/Requirements.fs
@@ -0,0 +1,78 @@
+namespace ARCtrl.CWL
+
+open DynamicObj
+
+type DockerRequirement = {
+ DockerPull: string option
+ DockerFile: Map option
+ DockerImageId: string option
+}
+
+/// Define an environment variable that will be set in the runtime environment by the workflow platform when executing the command line tool.
+type EnvironmentDef = {
+ EnvName: string
+ EnvValue: string
+}
+
+/// "min" is the minimum amount of a resource that must be reserved to schedule a job. If "min" cannot be satisfied, the job should not be run.
+/// "max" is the maximum amount of a resource that the job shall be permitted to use. If a node has sufficient resources, multiple jobs may be scheduled on a single node provided each job's "max" resource requirements are met.
+/// If a job attempts to exceed its "max" resource allocation, an implementation may deny additional resources, which may result in job failure.
+/// If "min" is specified but "max" is not, then "max" == "min" If "max" is specified by "min" is not, then "min" == "max".
+/// It is an error if max < min.
+/// It is an error if the value of any of these fields is negative.
+/// If neither "min" nor "max" is specified for a resource, default values are used.
+type ResourceRequirementInstance (
+ ?coresMin,
+ ?coresMax,
+ ?ramMin,
+ ?ramMax,
+ ?tmpdirMin,
+ ?tmpdirMax,
+ ?outdirMin,
+ ?outdirMax
+) as this =
+ inherit DynamicObj ()
+ do
+ DynObj.setOptionalProperty (nameof coresMin) coresMin this
+ DynObj.setOptionalProperty (nameof coresMax) coresMax this
+ DynObj.setOptionalProperty (nameof ramMin) ramMin this
+ DynObj.setOptionalProperty (nameof ramMax) ramMax this
+ DynObj.setOptionalProperty (nameof tmpdirMin) tmpdirMin this
+ DynObj.setOptionalProperty (nameof tmpdirMax) tmpdirMax this
+ DynObj.setOptionalProperty (nameof outdirMin) outdirMin this
+ DynObj.setOptionalProperty (nameof outdirMax) outdirMax this
+
+type Requirement =
+ /// Indicates that the workflow platform must support inline Javascript expressions.
+ | InlineJavascriptRequirement
+ /// This field consists of an array of type definitions which must be used when interpreting the inputs and outputs fields.
+ | SchemaDefRequirement of ResizeArray
+ /// Indicates that a workflow component should be run in a Docker or Docker-compatible (such as Singularity and udocker) container environment and specifies how to fetch or build the image.
+ | DockerRequirement of DockerRequirement
+ /// A list of software packages that should be configured in the environment of the defined process.
+ | SoftwareRequirement of ResizeArray
+ /// Define a list of files and subdirectories that must be created by the workflow platform in the designated output directory prior to executing the command line tool.
+ | InitialWorkDirRequirement of ResizeArray
+ /// Define a list of environment variables which will be set in the execution environment of the tool. See EnvironmentDef for details.
+ | EnvVarRequirement of ResizeArray
+ /// Modify the behavior of CommandLineTool to generate a single string containing a shell command line.
+ | ShellCommandRequirement
+ /// Specify basic hardware resource requirements.
+ | ResourceRequirement of ResourceRequirementInstance
+ /// For implementations that support reusing output from past work (on the assumption that same code and same input produce same results), control whether to enable or disable the reuse behavior for a particular tool or step.
+ | WorkReuseRequirement
+ /// Indicate whether a process requires outgoing IPv4/IPv6 network access. Choice of IPv4 or IPv6 is implementation and site specific, correct tools must support both.
+ | NetworkAccessRequirement
+ /// If inplaceUpdate is true, then an implementation supporting this feature may permit tools to directly update files with writable: true in InitialWorkDirRequirement.
+ | InplaceUpdateRequirement
+ /// Set an upper limit on the execution time of a CommandLineTool.
+ | ToolTimeLimitRequirement of float
+ /// Indicates that the workflow platform must support nested workflows in the run field of WorkflowStep.
+ | SubworkflowFeatureRequirement
+ /// Indicates that the workflow platform must support the scatter and scatterMethod fields of WorkflowStep.
+ | ScatterFeatureRequirement
+ /// Indicates that the workflow platform must support multiple inbound data links listed in the source field of WorkflowStepInput.
+ | MultipleInputFeatureRequirement
+ /// Indicate that the workflow platform must support the valueFrom field of WorkflowStepInput.
+ | StepInputExpressionRequirement
+
diff --git a/src/CWL/ToolDescription.fs b/src/CWL/ToolDescription.fs
new file mode 100644
index 00000000..ce2e8b7c
--- /dev/null
+++ b/src/CWL/ToolDescription.fs
@@ -0,0 +1,52 @@
+namespace ARCtrl.CWL
+
+open DynamicObj
+open Fable.Core
+
+[]
+type CWLToolDescription (
+ outputs: ResizeArray,
+ ?cwlVersion: string,
+ ?baseCommand: ResizeArray,
+ ?requirements: ResizeArray,
+ ?hints: ResizeArray,
+ ?inputs: ResizeArray,
+ ?metadata: DynamicObj
+ ) =
+ inherit DynamicObj ()
+
+ let mutable _cwlVersion: string = cwlVersion |> Option.defaultValue "v1.2"
+ let mutable _outputs: ResizeArray = outputs
+ let mutable _baseCommand: ResizeArray option = baseCommand
+ let mutable _requirements: ResizeArray option = requirements
+ let mutable _hints: ResizeArray option = hints
+ let mutable _inputs: ResizeArray option = inputs
+ let mutable _metadata: DynamicObj option = metadata
+
+ member this.CWLVersion
+ with get() = _cwlVersion
+ and set(version) = _cwlVersion <- version
+
+ member this.Outputs
+ with get() = _outputs
+ and set(outputs) = _outputs <- outputs
+
+ member this.BaseCommand
+ with get() = _baseCommand
+ and set(baseCommand) = _baseCommand <- baseCommand
+
+ member this.Requirements
+ with get() = _requirements
+ and set(requirements) = _requirements <- requirements
+
+ member this.Hints
+ with get() = _hints
+ and set(hints) = _hints <- hints
+
+ member this.Inputs
+ with get() = _inputs
+ and set(inputs) = _inputs <- inputs
+
+ member this.Metadata
+ with get() = _metadata
+ and set(metadata) = _metadata <- metadata
\ No newline at end of file
diff --git a/src/CWL/WorkflowDescription.fs b/src/CWL/WorkflowDescription.fs
new file mode 100644
index 00000000..aa5664c1
--- /dev/null
+++ b/src/CWL/WorkflowDescription.fs
@@ -0,0 +1,53 @@
+namespace ARCtrl.CWL
+
+open ARCtrl.CWL
+open DynamicObj
+open Fable.Core
+
+[]
+type CWLWorkflowDescription(
+ steps: ResizeArray,
+ inputs: ResizeArray,
+ outputs: ResizeArray,
+ ?cwlVersion: string,
+ ?requirements: ResizeArray,
+ ?hints: ResizeArray,
+ ?metadata: DynamicObj
+) =
+ inherit DynamicObj()
+
+ let mutable _cwlVersion: string = cwlVersion |> Option.defaultValue "v1.2"
+ let mutable _steps: ResizeArray = steps
+ let mutable _inputs: ResizeArray = inputs
+ let mutable _outputs: ResizeArray = outputs
+ let mutable _requirements: ResizeArray option = requirements
+ let mutable _hints: ResizeArray option = hints
+ let mutable _metadata: DynamicObj option = metadata
+
+ member this.CWLVersion
+ with get() = _cwlVersion
+ and set(version) = _cwlVersion <- version
+
+ member this.Steps
+ with get() = _steps
+ and set(steps) = _steps <- steps
+
+ member this.Inputs
+ with get() = _inputs
+ and set(inputs) = _inputs <- inputs
+
+ member this.Outputs
+ with get() = _outputs
+ and set(outputs) = _outputs <- outputs
+
+ member this.Requirements
+ with get() = _requirements
+ and set(requirements) = _requirements <- requirements
+
+ member this.Hints
+ with get() = _hints
+ and set(hints) = _hints <- hints
+
+ member this.Metadata
+ with get() = _metadata
+ and set(metadata) = _metadata <- metadata
\ No newline at end of file
diff --git a/src/CWL/WorkflowSteps.fs b/src/CWL/WorkflowSteps.fs
new file mode 100644
index 00000000..3e4ae125
--- /dev/null
+++ b/src/CWL/WorkflowSteps.fs
@@ -0,0 +1,59 @@
+namespace ARCtrl.CWL
+
+open DynamicObj
+open Fable.Core
+
+type StepInput = {
+ Id: string
+ Source: string option
+ DefaultValue: string option
+ ValueFrom: string option
+}
+
+type StepOutput = {
+ Id: ResizeArray
+}
+
+[]
+type WorkflowStep (
+ id: string,
+ in_: ResizeArray,
+ out_: StepOutput,
+ run: string,
+ ?requirements: ResizeArray,
+ ?hints: ResizeArray
+) =
+ inherit DynamicObj ()
+
+ let mutable _id: string = id
+ let mutable _in: ResizeArray = in_
+ let mutable _out: StepOutput = out_
+ let mutable _run: string = run
+ let mutable _requirements: ResizeArray option = requirements
+ let mutable _hints: ResizeArray option = hints
+
+ member this.Id
+ with get() = _id
+ and set(id) = _id <- id
+
+ member this.In
+ with get() = _in
+ and set(in_) = _in <- in_
+
+ member this.Out
+ with get() = _out
+ and set(out_) = _out <- out_
+
+ member this.Run
+ with get() = _run
+ and set(run) = _run <- run
+
+ member this.Requirements
+ with get() = _requirements
+ and set(requirements) = _requirements <- requirements
+
+ member this.Hints
+ with get() = _hints
+ and set(hints) = _hints <- hints
+
+
diff --git a/src/Package.Metadata.props b/src/Package.Metadata.props
index 8b7e3091..a3ec1c0c 100644
--- a/src/Package.Metadata.props
+++ b/src/Package.Metadata.props
@@ -7,7 +7,7 @@
- nfdi4plants, Lukas Weil, Kevin Frey, Kevin Schneider, Florian Wetzels
+ nfdi4plants, Lukas Weil, Kevin Frey, Kevin Schneider, Florian Wetzels, Caroline Ott
MIT
logo.png
https://github.com/nfdi4plants/ARCtrl
diff --git a/tests/All/All.Tests.fsproj b/tests/All/All.Tests.fsproj
index 3a59e902..8bca097a 100644
--- a/tests/All/All.Tests.fsproj
+++ b/tests/All/All.Tests.fsproj
@@ -19,6 +19,7 @@
+
diff --git a/tests/All/Main.fs b/tests/All/Main.fs
index 7c62a36b..105f41d3 100644
--- a/tests/All/Main.fs
+++ b/tests/All/Main.fs
@@ -13,7 +13,7 @@ let all = testList "All" [
ARCtrl.ValidationPackages.Tests.all
ARCtrl.Contract.Tests.all
ARCtrl.ROCrate.Tests.all
-
+ ARCtrl.CWL.Tests.all
]
[]
diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj
new file mode 100644
index 00000000..495bc135
--- /dev/null
+++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj
@@ -0,0 +1,22 @@
+
+
+
+ Exe
+ net8.0
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs
new file mode 100644
index 00000000..238ab408
--- /dev/null
+++ b/tests/CWL/CWLObject.Tests.fs
@@ -0,0 +1,257 @@
+module Tests.CWLObject
+
+open ARCtrl.CWL
+open TestingUtils
+open DynamicObj
+
+let decodeCWLToolDescription: CWLToolDescription =
+ TestObjects.CWL.CommandLineTool.cwlFile
+ |> Decode.decodeCommandLineTool
+
+let decodeCWLToolDescriptionMetadata: CWLToolDescription =
+ TestObjects.CWL.CommandLineToolMetadata.cwlFile
+ |> Decode.decodeCommandLineTool
+
+
+let testCWLToolDescription =
+ testList "Decode" [
+ testCase "CWLVersion" <| fun _ ->
+ let expected = "v1.2"
+ let actual = decodeCWLToolDescription.CWLVersion
+ Expect.equal actual expected ""
+ testCase "baseCommand" <| fun _ ->
+ let expected = Some (ResizeArray [|"dotnet"; "fsi"; "script.fsx"|])
+ let actual = decodeCWLToolDescription.BaseCommand
+ Expect.sequenceEqual actual.Value expected.Value ""
+ testList "Hints" [
+ let hintsItem = decodeCWLToolDescription.Hints
+ testCase "DockerRequirement" <| fun _ ->
+ let expected = DockerRequirement {DockerPull = Some "mcr.microsoft.com/dotnet/sdk:6.0"; DockerFile = None; DockerImageId = None}
+ let actual = hintsItem.Value.[0]
+ Expect.equal actual expected ""
+ ]
+ testList "Requirements" [
+ let requirementsItem = decodeCWLToolDescription.Requirements
+ testCase "InitialWorkDirRequirement" <| fun _ ->
+ let expected = InitialWorkDirRequirement (ResizeArray [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|])
+ let actual = requirementsItem.Value.[0]
+ match actual, expected with
+ | InitialWorkDirRequirement actualType, InitialWorkDirRequirement expectedType ->
+ Expect.sequenceEqual actualType expectedType ""
+ | _ -> failwith "This test case can only be InitialWorkDirRequirement"
+ testCase "EnvVarRequirement" <| fun _ ->
+ let expected = EnvVarRequirement (ResizeArray [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|])
+ let actual = requirementsItem.Value.[1]
+ match actual, expected with
+ | EnvVarRequirement actualType, EnvVarRequirement expectedType ->
+ Expect.sequenceEqual actualType expectedType ""
+ | _ -> failwith "This test case can only be EnvVarRequirement"
+ testCase "NetworkAccessRequirement" <| fun _ ->
+ let expected = NetworkAccessRequirement
+ let actual = requirementsItem.Value.[2]
+ Expect.equal actual expected ""
+ ]
+ testList "Inputs" [
+ let inputsItem = decodeCWLToolDescription.Inputs.Value
+ testCase "Length" <| fun _ ->
+ let expected = 2
+ let actual = inputsItem.Count
+ Expect.equal actual expected ""
+ testList "File" [
+ let fileItem = inputsItem.[0]
+ testCase "Name" <| fun _ ->
+ let expected = "firstArg"
+ let actual = fileItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = File (FileInstance())
+ let actual = fileItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "InputBinding" <| fun _ ->
+ let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None}
+ let actual = fileItem.InputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "String" [
+ let stringItem = inputsItem.[1]
+ testCase "Name" <| fun _ ->
+ let expected = "secondArg"
+ let actual = stringItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = String
+ let actual = stringItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "InputBinding" <| fun _ ->
+ let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None}
+ let actual = stringItem.InputBinding
+ Expect.equal actual expected ""
+ ]
+ ]
+ testList "Outputs" [
+ let outputsItem = decodeCWLToolDescription.Outputs
+ testCase "Length" <| fun _ ->
+ let expected = 2
+ let actual = outputsItem.Count
+ Expect.equal actual expected ""
+ testList "Directory" [
+ let directoryItem = outputsItem.[0]
+ testCase "Name" <| fun _ ->
+ let expected = "output"
+ let actual = directoryItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = Directory (DirectoryInstance())
+ let actual = directoryItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"}
+ let actual = directoryItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "File" [
+ let fileItem = outputsItem.[1]
+ testCase "Name" <| fun _ ->
+ let expected = "output2"
+ let actual = fileItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = File (FileInstance())
+ let actual = fileItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"}
+ let actual = fileItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ ]
+ testCase "Metadata" <| fun _ ->
+ let expected = None
+ let actual = decodeCWLToolDescription.Metadata
+ Expect.equal actual expected ""
+ ]
+
+let testCWLToolDescriptionMetadata =
+ testList "Decode with Metadata" [
+ testCase "CWLVersion" <| fun _ ->
+ let expected = "v1.2"
+ let actual = decodeCWLToolDescriptionMetadata.CWLVersion
+ Expect.equal actual expected ""
+ testCase "baseCommand" <| fun _ ->
+ let expected = Some (ResizeArray [|"dotnet"; "fsi"; "script.fsx"|])
+ let actual = decodeCWLToolDescriptionMetadata.BaseCommand
+ Expect.sequenceEqual actual.Value expected.Value ""
+ testList "Hints" [
+ let hintsItem = decodeCWLToolDescriptionMetadata.Hints
+ testCase "DockerRequirement" <| fun _ ->
+ let expected = DockerRequirement {DockerPull = Some "mcr.microsoft.com/dotnet/sdk:6.0"; DockerFile = None; DockerImageId = None}
+ let actual = hintsItem.Value.[0]
+ Expect.equal actual expected ""
+ ]
+ testList "Requirements" [
+ let requirementsItem = decodeCWLToolDescriptionMetadata.Requirements
+ testCase "InitialWorkDirRequirement" <| fun _ ->
+ let expected = InitialWorkDirRequirement (ResizeArray [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|])
+ let actual = requirementsItem.Value.[0]
+ match actual, expected with
+ | InitialWorkDirRequirement actualType, InitialWorkDirRequirement expectedType ->
+ Expect.sequenceEqual actualType expectedType ""
+ | _ -> failwith "This test case can only be InitialWorkDirRequirement"
+ testCase "EnvVarRequirement" <| fun _ ->
+ let expected = EnvVarRequirement (ResizeArray [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|])
+ let actual = requirementsItem.Value.[1]
+ match actual, expected with
+ | EnvVarRequirement actualType, EnvVarRequirement expectedType ->
+ Expect.sequenceEqual actualType expectedType ""
+ | _ -> failwith "This test case can only be EnvVarRequirement"
+ testCase "NetworkAccessRequirement" <| fun _ ->
+ let expected = NetworkAccessRequirement
+ let actual = requirementsItem.Value.[2]
+ Expect.equal actual expected ""
+ ]
+ testList "Inputs" [
+ let inputsItem = decodeCWLToolDescriptionMetadata.Inputs.Value
+ testCase "Length" <| fun _ ->
+ let expected = 2
+ let actual = inputsItem.Count
+ Expect.equal actual expected ""
+ testList "File" [
+ let fileItem = inputsItem.[0]
+ testCase "Name" <| fun _ ->
+ let expected = "firstArg"
+ let actual = fileItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = File (FileInstance())
+ let actual = fileItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "InputBinding" <| fun _ ->
+ let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None}
+ let actual = fileItem.InputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "String" [
+ let stringItem = inputsItem.[1]
+ testCase "Name" <| fun _ ->
+ let expected = "secondArg"
+ let actual = stringItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = String
+ let actual = stringItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "InputBinding" <| fun _ ->
+ let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None}
+ let actual = stringItem.InputBinding
+ Expect.equal actual expected ""
+ ]
+ ]
+ testList "Outputs" [
+ let outputsItem = decodeCWLToolDescriptionMetadata.Outputs
+ testCase "Length" <| fun _ ->
+ let expected = 2
+ let actual = outputsItem.Count
+ Expect.equal actual expected ""
+ testList "Directory" [
+ let directoryItem = outputsItem.[0]
+ testCase "Name" <| fun _ ->
+ let expected = "output"
+ let actual = directoryItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = Directory (DirectoryInstance())
+ let actual = directoryItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"}
+ let actual = directoryItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "File" [
+ let fileItem = outputsItem.[1]
+ testCase "Name" <| fun _ ->
+ let expected = "output2"
+ let actual = fileItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = File (FileInstance())
+ let actual = fileItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"}
+ let actual = fileItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ ]
+ testCase "Metadata" <| fun _ ->
+ Expect.isSome decodeCWLToolDescriptionMetadata.Metadata $"Expected {decodeCWLToolDescriptionMetadata.Metadata} to be Some"
+ let expected = TestObjects.CWL.CommandLineToolMetadata.expectedMetadataString.Trim().Replace("\r\n", "\n")
+ let actual = (decodeCWLToolDescriptionMetadata.Metadata.Value |> DynObj.format).Trim().Replace("\r\n", "\n")
+ Expect.equal actual expected ""
+ ]
+
+let main =
+ testList "CWLToolDescription" [
+ testCWLToolDescription
+ testCWLToolDescriptionMetadata
+ ]
\ No newline at end of file
diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs
new file mode 100644
index 00000000..da53ae8c
--- /dev/null
+++ b/tests/CWL/CWLWorkflow.Tests.fs
@@ -0,0 +1,108 @@
+module Tests.CWLWorkflow
+
+open ARCtrl.CWL
+open TestingUtils
+
+let decodeCWLWorkflowDescription: CWLWorkflowDescription =
+ TestObjects.CWL.Workflow.workflowFile
+ |> Decode.decodeWorkflow
+
+let testCWLWorkflowDescription =
+ testList "Decode" [
+ testCase "CWLVersion" <| fun _ ->
+ let expected = "v1.2"
+ let actual = decodeCWLWorkflowDescription.CWLVersion
+ Expect.equal actual expected ""
+ testCase "MultipleInputFeatureRequirement" <| fun _ ->
+ let requirementsItem = decodeCWLWorkflowDescription.Requirements
+ let expected = MultipleInputFeatureRequirement
+ let actual = requirementsItem.Value.[0]
+ Expect.equal actual expected ""
+ testCase "inputs" <| fun _ ->
+ let expected = ResizeArray [|
+ CWLInput("cores", CWLType.Int);
+ CWLInput("db", CWLType.File (FileInstance()));
+ CWLInput ("stage", CWLType.Directory (DirectoryInstance()));
+ CWLInput ("outputMzML", CWLType.Directory (DirectoryInstance()));
+ CWLInput ("outputPSM", CWLType.Directory (DirectoryInstance()));
+ CWLInput ("inputMzML", CWLType.Directory (DirectoryInstance()));
+ CWLInput ("paramsMzML", CWLType.File (FileInstance()));
+ CWLInput ("paramsPSM", CWLType.File (FileInstance()))
+ |]
+ let actual = decodeCWLWorkflowDescription.Inputs
+ for i = 0 to actual.Count - 1 do
+ Expect.equal actual.[i].Name expected.[i].Name ""
+ Expect.equal actual.[i].InputBinding expected.[i].InputBinding ""
+ Expect.equal actual.[i].Type_ expected.[i].Type_ ""
+ testList "steps" [
+ let workflowSteps = decodeCWLWorkflowDescription.Steps
+ testList "IDs" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = "MzMLToMzlite"
+ let actual = workflowSteps.[0].Id
+ Expect.equal actual expected ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = "PeptideSpectrumMatching"
+ let actual = workflowSteps.[1].Id
+ Expect.equal actual expected ""
+ ]
+ testList "Run" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = "./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl"
+ let actual = workflowSteps.[0].Run
+ Expect.equal actual expected ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = "./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl"
+ let actual = workflowSteps.[1].Run
+ Expect.equal actual expected ""
+ ]
+ testList "In" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = ResizeArray [|
+ {Id = "stageDirectory"; Source = Some "stage"; DefaultValue = None; ValueFrom = None};
+ {Id = "inputDirectory"; Source = Some "inputMzML"; DefaultValue = None; ValueFrom = None};
+ {Id = "params"; Source = Some "paramsMzML"; DefaultValue = None; ValueFrom = None};
+ {Id = "outputDirectory"; Source = Some "outputMzML"; DefaultValue = None; ValueFrom = None};
+ {Id = "parallelismLevel"; Source = Some "cores"; DefaultValue = None; ValueFrom = None}
+ |]
+ let actual = workflowSteps.[0].In
+ Expect.sequenceEqual actual expected ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = ResizeArray [|
+ {Id = "stageDirectory"; Source = Some "stage"; DefaultValue = None; ValueFrom = None};
+ {Id = "inputDirectory"; Source = Some "MzMLToMzlite/dir"; DefaultValue = None; ValueFrom = None };
+ {Id = "database"; Source = Some "db"; DefaultValue = None; ValueFrom = None};
+ {Id = "params"; Source = Some "paramsPSM"; DefaultValue = None; ValueFrom = None};
+ {Id = "outputDirectory"; Source = Some "outputPSM"; DefaultValue = None; ValueFrom = None}
+ {Id = "parallelismLevel"; Source = Some "cores"; DefaultValue = None; ValueFrom = None};
+ |]
+ let actual = workflowSteps.[1].In
+ Expect.sequenceEqual actual expected ""
+ ]
+ testList "Out" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = {Id = ResizeArray [|"dir"|]}
+ let actual = workflowSteps.[0].Out
+ Expect.sequenceEqual actual.Id expected.Id ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = {Id = ResizeArray [|"dir"|]}
+ let actual = workflowSteps.[1].Out
+ Expect.sequenceEqual actual.Id expected.Id ""
+ ]
+ ]
+ testCase "outputs" <| fun _ ->
+ let expected = ResizeArray [|
+ CWLOutput("mzlite", CWLType.Directory (DirectoryInstance()), outputSource = "MzMLToMzlite/dir");
+ CWLOutput("psm", CWLType.Directory (DirectoryInstance()), outputSource = "PeptideSpectrumMatching/dir")
+ |]
+ let actual = decodeCWLWorkflowDescription.Outputs
+ for i = 0 to actual.Count - 1 do
+ Expect.equal actual.[i].Name expected.[i].Name ""
+ Expect.equal actual.[i].OutputBinding expected.[i].OutputBinding ""
+ Expect.equal actual.[i].Type_ expected.[i].Type_ ""
+ ]
+
+let main =
+ testList "CWLWorkflowDescription" [
+ testCWLWorkflowDescription
+ ]
\ No newline at end of file
diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs
new file mode 100644
index 00000000..55baee87
--- /dev/null
+++ b/tests/CWL/Inputs.Tests.fs
@@ -0,0 +1,56 @@
+module Tests.Inputs
+
+open ARCtrl.CWL
+open YAMLicious
+open TestingUtils
+
+let decodeInput =
+ TestObjects.CWL.Inputs.inputsFileContent
+ |> Decode.read
+ |> Decode.inputsDecoder
+ |>fun i ->i.Value
+
+let testInput =
+ testList "Decode" [
+ testCase "Length" <| fun _ -> Expect.equal 5 decodeInput.Count ""
+ testList "Directory" [
+ let directoryItem = decodeInput.[0]
+ testCase "Name" <| fun _ -> Expect.equal "arcDirectory" directoryItem.Name ""
+ testCase "Type" <| fun _ -> Expect.equal (Directory (DirectoryInstance())) directoryItem.Type_.Value ""
+ ]
+ testList "File" [
+ let fileItem = decodeInput.[1]
+ testCase "Name" <| fun _ -> Expect.equal "firstArg" fileItem.Name ""
+ testCase "Type" <| fun _ -> Expect.equal (File (FileInstance())) fileItem.Type_.Value ""
+ testCase "InputBinding" <| fun _ -> Expect.equal (Some {Position = Some 1; Prefix = Some "--example"; ItemSeparator = None; Separate = None}) fileItem.InputBinding ""
+ ]
+ testList "File optional" [
+ let fileItem = decodeInput.[2]
+ testCase "Name" <| fun _ -> Expect.equal "argOptional" fileItem.Name ""
+ testCase "Type" <| fun _ -> Expect.equal (File (FileInstance())) fileItem.Type_.Value ""
+ testCase "Optional" <| fun _ -> Expect.equal (Some true) fileItem.Optional ""
+ ]
+ testList "File array optional" [
+ let fileItem = decodeInput.[3]
+ testCase "Name" <| fun _ -> Expect.equal "argOptionalMap" fileItem.Name ""
+ testCase "Type" <| fun _ -> Expect.equal (Array (File (FileInstance()))) fileItem.Type_.Value ""
+ testCase "Optional" <| fun _ -> Expect.equal (Some true) fileItem.Optional ""
+ ]
+ testList "String" [
+ let stringItem = decodeInput.[4]
+ testCase "Name" <| fun _ -> Expect.equal "secondArg" stringItem.Name ""
+ testCase "Type" <| fun _ ->
+ let expected = String
+ let actual = stringItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "InputBinding" <| fun _ ->
+ let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = Some false}
+ let actual = stringItem.InputBinding
+ Expect.equal actual expected ""
+ ]
+ ]
+
+let main =
+ testList "Input" [
+ testInput
+ ]
\ No newline at end of file
diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs
new file mode 100644
index 00000000..c08a8276
--- /dev/null
+++ b/tests/CWL/Main.fs
@@ -0,0 +1,18 @@
+module ARCtrl.CWL.Tests
+
+open Fable.Pyxpecto
+
+let all = testSequenced <| testList "CWL" [
+ Tests.CWLWorkflow.main
+ Tests.CWLObject.main
+ Tests.Metadata.main
+ Tests.Outputs.main
+ Tests.Inputs.main
+ Tests.Requirements.main
+ Tests.WorkflowSteps.main
+]
+
+#if !TESTS_ALL
+[]
+#endif
+let main argv = Pyxpecto.runTests [||] all
diff --git a/tests/CWL/Metadata.Tests.fs b/tests/CWL/Metadata.Tests.fs
new file mode 100644
index 00000000..53fe2e69
--- /dev/null
+++ b/tests/CWL/Metadata.Tests.fs
@@ -0,0 +1,39 @@
+module Tests.Metadata
+
+open ARCtrl.CWL.Decode
+open DynamicObj
+open YAMLicious
+open TestingUtils
+
+let decodeMetadata =
+ TestObjects.CWL.Metadata.metadataFileContent
+ |> Decode.read
+
+let overflowDictionary =
+ decodeMetadata
+ |> Decode.object (fun get -> get.Overflow.FieldList [])
+
+let dynObj =
+ overflowDecoder (new DynamicObj()) overflowDictionary
+
+let testMetadata =
+ testList "Decode" [
+ testCase "Overflow Dictionary Keys" <| fun _ ->
+ let expected = ["arc:has technology type"; "arc:technology platform"; "arc:performer"; "arc:has process sequence"]
+ let actual = overflowDictionary.Keys |> List.ofSeq
+ Expect.equal actual expected ""
+ testCase "DynObj Keys" <| fun _ ->
+ let expected = ["arc:has technology type"; "arc:technology platform"; "arc:performer"; "arc:has process sequence"]
+ let actual = dynObj.GetProperties(false) |> List.ofSeq |> List.map (fun x -> x.Key)
+ Expect.equal actual expected ""
+ testCase "DynObj setProperty Value check" <| fun _ ->
+ let expectedValue = ".NET"
+ let actualValue = dynObj |> DynObj.tryGetTypedPropertyValue "arc:technology platform"
+ Expect.equal actualValue.Value expectedValue ""
+ ]
+
+
+let main =
+ testList "DynamicObj Metadata" [
+ testMetadata
+ ]
\ No newline at end of file
diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs
new file mode 100644
index 00000000..4fa6cb7a
--- /dev/null
+++ b/tests/CWL/Outputs.Tests.fs
@@ -0,0 +1,98 @@
+module Tests.Outputs
+
+open ARCtrl.CWL
+open YAMLicious
+open TestingUtils
+
+let decodeOutput =
+ TestObjects.CWL.Outputs.outputsFileContent
+ |> Decode.read
+ |> Decode.outputsDecoder
+
+let testOutput =
+ testList "Decode" [
+ testCase "Length" <| fun _ ->
+ let expected = 5
+ let actual = decodeOutput.Count
+ Expect.equal actual expected ""
+ testList "File" [
+ let fileItem = decodeOutput.[0]
+ testCase "Name" <| fun _ ->
+ let expected = "output"
+ let actual = fileItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = File (FileInstance())
+ let actual = fileItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "./arc/runs/fsResult1/result.csv"}
+ let actual = fileItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "Directory" [
+ let directoryItem = decodeOutput.[1]
+ testCase "Name" <| fun _ ->
+ let expected = "example1"
+ let actual = directoryItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = Directory (DirectoryInstance())
+ let actual = directoryItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"}
+ let actual = directoryItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "Directory 2" [
+ let directoryItem = decodeOutput.[2]
+ testCase "Name" <| fun _ ->
+ let expected = "example2"
+ let actual = directoryItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = Directory (DirectoryInstance())
+ let actual = directoryItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = None
+ let actual = directoryItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "File Array" [
+ let fileArrayItem = decodeOutput.[3]
+ testCase "Name" <| fun _ ->
+ let expected = "exampleArray1"
+ let actual = fileArrayItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = Array (File (FileInstance()))
+ let actual = fileArrayItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"}
+ let actual = fileArrayItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ testList "File Array 2" [
+ let fileArrayItem = decodeOutput.[4]
+ testCase "Name" <| fun _ ->
+ let expected = "exampleArray2"
+ let actual = fileArrayItem.Name
+ Expect.equal actual expected ""
+ testCase "Type" <| fun _ ->
+ let expected = Array (File (FileInstance()))
+ let actual = fileArrayItem.Type_.Value
+ Expect.equal actual expected ""
+ testCase "OutputBinding" <| fun _ ->
+ let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"}
+ let actual = fileArrayItem.OutputBinding
+ Expect.equal actual expected ""
+ ]
+ ]
+
+let main =
+ testList "Output" [
+ testOutput
+ ]
\ No newline at end of file
diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs
new file mode 100644
index 00000000..2ad6af5c
--- /dev/null
+++ b/tests/CWL/Requirements.Tests.fs
@@ -0,0 +1,67 @@
+module Tests.Requirements
+
+open ARCtrl.CWL
+open YAMLicious
+open TestingUtils
+
+let decodeRequirement =
+ TestObjects.CWL.Requirements.requirementsFileContent
+ |> Decode.read
+ |> Decode.requirementsDecoder
+ |> fun r -> r.Value
+
+let testRequirement =
+ testList "Decode" [
+ testCase "Length" <| fun _ -> Expect.equal 5 decodeRequirement.Count ""
+ testList "DockerRequirement" [
+ let dockerItem = decodeRequirement.[0]
+ testCase "Class" <| fun _ ->
+ let expected = DockerRequirement {DockerPull = None; DockerFile = Some (Map [("$include", "FSharpArcCapsule/Dockerfile")]); DockerImageId = Some "devcontainer"}
+ let actual = dockerItem
+ Expect.equal actual expected ""
+ ]
+ testList "InitialWorkDirRequirement" [
+ let initialWorkDirItem = decodeRequirement.[1]
+ testCase "Class" <| fun _ ->
+ let expected = InitialWorkDirRequirement (ResizeArray [|Dirent {Entryname = Some "arc"; Entry = "$(inputs.arcDirectory)"; Writable = Some true}; Dirent {Entryname = None; Entry = "$(inputs.outputDirectory)"; Writable = Some true}|])
+ let actual = initialWorkDirItem
+ match actual, expected with
+ | InitialWorkDirRequirement actualType, InitialWorkDirRequirement expectedType ->
+ Expect.sequenceEqual actualType expectedType ""
+ | _ -> failwith "This test case can only be InitialWorkDirRequirement"
+ ]
+ testList "EnvVarRequirement" [
+ let envVarItem = decodeRequirement.[2]
+ testCase "Class" <| fun _ ->
+ let expected = EnvVarRequirement (ResizeArray [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}; {EnvName = "TEST"; EnvValue = "false"}|])
+ let actual = envVarItem
+ match actual, expected with
+ | EnvVarRequirement actualType, EnvVarRequirement expectedType ->
+ Expect.sequenceEqual actualType expectedType ""
+ | _ -> failwith "This test case can only be EnvVarRequirement"
+ ]
+ testList "SoftwareRequirement" [
+ let softwareItem = decodeRequirement.[3]
+ testCase "Class" <| fun _ ->
+ let expected = SoftwareRequirement (ResizeArray [|{Package = "interproscan"; Specs = Some (ResizeArray [| "https://identifiers.org/rrid/RRID:SCR_005829" |]); Version = Some (ResizeArray[| "5.21-60" |])}|])
+ let actual = softwareItem
+ match actual, expected with
+ | SoftwareRequirement actualType, SoftwareRequirement expectedType ->
+ Expect.equal actualType.[0].Package expectedType.[0].Package ""
+ Expect.sequenceEqual actualType.[0].Specs.Value expectedType.[0].Specs.Value ""
+ Expect.sequenceEqual actualType.[0].Version.Value expectedType.[0].Version.Value ""
+ | _ -> failwith "This test case can only be SoftwareRequirement"
+ ]
+ testList "NetworkAccess" [
+ let networkAccessItem = decodeRequirement.[4]
+ testCase "Class" <| fun _ ->
+ let expected = NetworkAccessRequirement
+ let actual = networkAccessItem
+ Expect.equal actual expected ""
+ ]
+ ]
+
+let main =
+ testList "Requirement" [
+ testRequirement
+ ]
\ No newline at end of file
diff --git a/tests/CWL/WorkflowSteps.Tests.fs b/tests/CWL/WorkflowSteps.Tests.fs
new file mode 100644
index 00000000..78219d2b
--- /dev/null
+++ b/tests/CWL/WorkflowSteps.Tests.fs
@@ -0,0 +1,67 @@
+module Tests.WorkflowSteps
+
+open ARCtrl.CWL
+open YAMLicious
+open TestingUtils
+
+let decodeWorkflowStep =
+ TestObjects.CWL.WorkflowSteps.workflowStepsFileContent
+ |> Decode.read
+ |> Decode.stepsDecoder
+
+let testWorkflowStep =
+ testList "Decode" [
+ testCase "Length" <| fun _ -> Expect.equal 2 decodeWorkflowStep.Count ""
+ testList "IDs" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = "MzMLToMzlite"
+ let actual = decodeWorkflowStep.[0].Id
+ Expect.equal actual expected ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = "PeptideSpectrumMatching"
+ let actual = decodeWorkflowStep.[1].Id
+ Expect.equal actual expected ""
+ ]
+ testList "Run" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = "./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl"
+ let actual = decodeWorkflowStep.[0].Run
+ Expect.equal actual expected ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = "./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl"
+ let actual = decodeWorkflowStep.[1].Run
+ Expect.equal actual expected ""
+ ]
+ testList "In" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = ResizeArray [|
+ {Id = "stageDirectory"; Source = Some "stage"; DefaultValue = None; ValueFrom = None};
+ {Id = "inputDirectory"; Source = Some "inputMzML"; DefaultValue = None; ValueFrom = None}
+ |]
+ let actual = decodeWorkflowStep.[0].In
+ Expect.sequenceEqual actual expected ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = ResizeArray [|
+ {Id = "stageDirectory"; Source = Some "stage"; DefaultValue = None; ValueFrom = None};
+ {Id = "inputDirectory"; Source = Some "MzMLToMzlite/dir"; DefaultValue = None; ValueFrom = None };
+ {Id = "parallelismLevel"; Source = None; DefaultValue = Some "8"; ValueFrom = None};
+ {Id = "outputDirectory"; Source = None; DefaultValue = None; ValueFrom = Some "output"}|]
+ let actual = decodeWorkflowStep.[1].In
+ Expect.sequenceEqual actual expected ""
+ ]
+ testList "Out" [
+ testCase "MzMLToMzlite" <| fun _ ->
+ let expected = {Id = ResizeArray [|"dir"|]}
+ let actual = decodeWorkflowStep.[0].Out
+ Expect.sequenceEqual actual.Id expected.Id ""
+ testCase "PeptideSpectrumMatching" <| fun _ ->
+ let expected = {Id = ResizeArray [|"dir1";"dir2"|]}
+ let actual = decodeWorkflowStep.[1].Out
+ Expect.sequenceEqual actual.Id expected.Id ""
+ ]
+ ]
+
+let main =
+ testList "WorkflowStep" [
+ testWorkflowStep
+ ]
\ No newline at end of file
diff --git a/tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs b/tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs
new file mode 100644
index 00000000..9593a0bc
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs
@@ -0,0 +1,39 @@
+module TestObjects.CWL.CommandLineTool
+
+let cwlFile ="""cwlVersion: v1.2
+class: CommandLineTool
+hints:
+ - class: DockerRequirement
+ dockerPull: mcr.microsoft.com/dotnet/sdk:6.0
+requirements:
+ - class: InitialWorkDirRequirement
+ listing:
+ - entryname: script.fsx
+ entry:
+ $include: script.fsx
+ - class: EnvVarRequirement
+ envDef:
+ - envName: DOTNET_NOLOGO
+ envValue: "true"
+ - class: NetworkAccess
+ networkAccess: true
+baseCommand: [dotnet, fsi, script.fsx]
+inputs:
+ firstArg:
+ type: File
+ inputBinding:
+ position: 1
+ secondArg:
+ type: string
+ inputBinding:
+ position: 2
+
+outputs:
+ output:
+ type: Directory
+ outputBinding:
+ glob: $(runtime.outdir)/.nuget
+ output2:
+ type: File
+ outputBinding:
+ glob: $(runtime.outdir)/*.csv"""
\ No newline at end of file
diff --git a/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs b/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs
new file mode 100644
index 00000000..5e0ba034
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs
@@ -0,0 +1,108 @@
+module TestObjects.CWL.CommandLineToolMetadata
+
+let cwlFile ="""cwlVersion: v1.2
+class: CommandLineTool
+hints:
+ - class: DockerRequirement
+ dockerPull: mcr.microsoft.com/dotnet/sdk:6.0
+requirements:
+ - class: InitialWorkDirRequirement
+ listing:
+ - entryname: script.fsx
+ entry:
+ $include: script.fsx
+ - class: EnvVarRequirement
+ envDef:
+ - envName: DOTNET_NOLOGO
+ envValue: "true"
+ - class: NetworkAccess
+ networkAccess: true
+baseCommand: [dotnet, fsi, script.fsx]
+inputs:
+ firstArg:
+ type: File
+ inputBinding:
+ position: 1
+ secondArg:
+ type: string
+ inputBinding:
+ position: 2
+outputs:
+ output:
+ type: Directory
+ outputBinding:
+ glob: $(runtime.outdir)/.nuget
+ output2:
+ type: File
+ outputBinding:
+ glob: $(runtime.outdir)/*.csv
+
+arc:has technology type:
+ - class: arc:technology type
+ arc:annotation value: "Fsharp Devcontainer"
+
+arc:technology platform: ".NET"
+
+arc:performer:
+ - class: arc:Person
+ arc:first name: "Timo"
+ arc:last name: "Mühlhaus"
+ arc:has role:
+ - class: arc:role
+ arc:term accession: "https://credit.niso.org/contributor-roles/formal-analysis/"
+ arc:annotation value: "Formal analysis"
+
+arc:has process sequence:
+ - class: arc:process sequence
+ arc:name: "script.fsx"
+ arc:has input:
+ - class: arc:data
+ arc:name: "./arc/assays/measurement1/dataset/table.csv"
+ arc:has parameter value:
+ - class: arc:process parameter value
+ arc:has parameter:
+ - class: arc:protocol parameter
+ arc:has parameter name:
+ - class: arc:parameter name
+ arc:term accession: "http://purl.obolibrary.org/obo/NCIT_C43582"
+ arc:term source REF: "NCIT"
+ arc:annotation value: "Data Transformation"
+ arc:value:
+ - class: arc:ontology annotation
+ arc:term accession: "http://purl.obolibrary.org/obo/NCIT_C64911"
+ arc:term source REF: "NCIT"
+ arc:annotation value: "Addition"
+"""
+
+let expectedMetadataString ="""?arc:has technology type:
+ ?class: arc:technology type
+ ?arc:annotation value: Fsharp Devcontainer
+?arc:technology platform: .NET
+?arc:performer:
+ ?class: arc:Person
+ ?arc:first name: Timo
+ ?arc:last name: Mühlhaus
+ ?arc:has role:
+ ?class: arc:role
+ ?arc:term accession: https://credit.niso.org/contributor-roles/formal-analysis/
+ ?arc:annotation value: Formal analysis
+?arc:has process sequence:
+ ?class: arc:process sequence
+ ?arc:name: script.fsx
+ ?arc:has input:
+ ?class: arc:data
+ ?arc:name: ./arc/assays/measurement1/dataset/table.csv
+ ?arc:has parameter value:
+ ?class: arc:process parameter value
+ ?arc:has parameter:
+ ?class: arc:protocol parameter
+ ?arc:has parameter name:
+ ?class: arc:parameter name
+ ?arc:term accession: http://purl.obolibrary.org/obo/NCIT_C43582
+ ?arc:term source REF: NCIT
+ ?arc:annotation value: Data Transformation
+ ?arc:value:
+ ?class: arc:ontology annotation
+ ?arc:term accession: http://purl.obolibrary.org/obo/NCIT_C64911
+ ?arc:term source REF: NCIT
+ ?arc:annotation value: Addition"""
\ No newline at end of file
diff --git a/tests/TestingUtils/TestObjects.CWL/Inputs.fs b/tests/TestingUtils/TestObjects.CWL/Inputs.fs
new file mode 100644
index 00000000..c040a6ce
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/Inputs.fs
@@ -0,0 +1,17 @@
+module TestObjects.CWL.Inputs
+
+let inputsFileContent ="""inputs:
+ arcDirectory: Directory
+ firstArg:
+ type: File
+ inputBinding:
+ position: 1
+ prefix: --example
+ argOptional:
+ type: File?
+ argOptionalMap: File[]?
+ secondArg:
+ type: string
+ inputBinding:
+ position: 2
+ separate: false"""
\ No newline at end of file
diff --git a/tests/TestingUtils/TestObjects.CWL/Metadata.fs b/tests/TestingUtils/TestObjects.CWL/Metadata.fs
new file mode 100644
index 00000000..6b0827ef
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/Metadata.fs
@@ -0,0 +1,38 @@
+module TestObjects.CWL.Metadata
+
+let metadataFileContent ="""arc:has technology type:
+ - class: arc:technology type
+ arc:annotation value: "Fsharp Devcontainer"
+
+arc:technology platform: ".NET"
+
+arc:performer:
+ - class: arc:Person
+ arc:first name: "Timo"
+ arc:last name: "Mühlhaus"
+ arc:has role:
+ - class: arc:role
+ arc:term accession: "https://credit.niso.org/contributor-roles/formal-analysis/"
+ arc:annotation value: "Formal analysis"
+
+arc:has process sequence:
+ - class: arc:process sequence
+ arc:name: "script.fsx"
+ arc:has input:
+ - class: arc:data
+ arc:name: "./arc/assays/measurement1/dataset/table.csv"
+ arc:has parameter value:
+ - class: arc:process parameter value
+ arc:has parameter:
+ - class: arc:protocol parameter
+ arc:has parameter name:
+ - class: arc:parameter name
+ arc:term accession: "http://purl.obolibrary.org/obo/NCIT_C43582"
+ arc:term source REF: "NCIT"
+ arc:annotation value: "Data Transformation"
+ arc:value:
+ - class: arc:ontology annotation
+ arc:term accession: "http://purl.obolibrary.org/obo/NCIT_C64911"
+ arc:term source REF: "NCIT"
+ arc:annotation value: "Addition"
+"""
\ No newline at end of file
diff --git a/tests/TestingUtils/TestObjects.CWL/Outputs.fs b/tests/TestingUtils/TestObjects.CWL/Outputs.fs
new file mode 100644
index 00000000..93311e79
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/Outputs.fs
@@ -0,0 +1,22 @@
+module TestObjects.CWL.Outputs
+
+let outputsFileContent ="""outputs:
+ output:
+ type: File
+ outputBinding:
+ glob: ./arc/runs/fsResult1/result.csv
+ example1:
+ type: Directory
+ outputBinding:
+ glob: ./arc/runs/fsResult1/example.csv
+ example2: Directory
+ exampleArray1:
+ type: File[]
+ outputBinding:
+ glob: ./arc/runs/fsResult1/example.csv
+ exampleArray2:
+ type:
+ type: array
+ items: File
+ outputBinding:
+ glob: ./arc/runs/fsResult1/example.csv"""
\ No newline at end of file
diff --git a/tests/TestingUtils/TestObjects.CWL/Requirements.fs b/tests/TestingUtils/TestObjects.CWL/Requirements.fs
new file mode 100644
index 00000000..3191e8b8
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/Requirements.fs
@@ -0,0 +1,26 @@
+module TestObjects.CWL.Requirements
+
+let requirementsFileContent ="""requirements:
+ - class: DockerRequirement
+ dockerImageId: "devcontainer"
+ dockerFile: {$include: "FSharpArcCapsule/Dockerfile"}
+ - class: InitialWorkDirRequirement
+ listing:
+ - entryname: arc
+ entry: $(inputs.arcDirectory)
+ writable: true
+ - entry: $(inputs.outputDirectory)
+ writable: true
+ - class: EnvVarRequirement
+ envDef:
+ - envName: DOTNET_NOLOGO
+ envValue: "true"
+ - envName: TEST
+ envValue: "false"
+ - class: SoftwareRequirement
+ packages:
+ - package: interproscan
+ specs: [ "https://identifiers.org/rrid/RRID:SCR_005829" ]
+ version: [ "5.21-60" ]
+ - class: NetworkAccess
+ networkAccess: true"""
\ No newline at end of file
diff --git a/tests/TestingUtils/TestObjects.CWL/Workflow.fs b/tests/TestingUtils/TestObjects.CWL/Workflow.fs
new file mode 100644
index 00000000..e082d3bd
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/Workflow.fs
@@ -0,0 +1,47 @@
+module TestObjects.CWL.Workflow
+
+let workflowFile ="""cwlVersion: v1.2
+class: Workflow
+
+requirements:
+ - class: MultipleInputFeatureRequirement
+
+inputs:
+ cores: int
+ db: File
+ stage: Directory
+ outputMzML: Directory
+ outputPSM: Directory
+ inputMzML: Directory
+ paramsMzML: File
+ paramsPSM: File
+
+steps:
+ MzMLToMzlite:
+ run: ./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl
+ in:
+ stageDirectory: stage
+ inputDirectory: inputMzML
+ params: paramsMzML
+ outputDirectory: outputMzML
+ parallelismLevel: cores
+ out: [dir]
+ PeptideSpectrumMatching:
+ run: ./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl
+ in:
+ stageDirectory: stage
+ inputDirectory: MzMLToMzlite/dir
+ database: db
+ params: paramsPSM
+ outputDirectory: outputPSM
+ parallelismLevel: cores
+ out: [dir]
+
+outputs:
+ mzlite:
+ type: Directory
+ outputSource: MzMLToMzlite/dir
+ psm:
+ type: Directory
+ outputSource: PeptideSpectrumMatching/dir"""
+
diff --git a/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs b/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs
new file mode 100644
index 00000000..f9a8d6c7
--- /dev/null
+++ b/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs
@@ -0,0 +1,23 @@
+module TestObjects.CWL.WorkflowSteps
+
+let workflowStepsFileContent ="""steps:
+ MzMLToMzlite:
+ run: ./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl
+ in:
+ stageDirectory: stage
+ inputDirectory:
+ source: inputMzML
+ out: [dir]
+ PeptideSpectrumMatching:
+ run: ./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl
+ in:
+ stageDirectory:
+ source: stage
+ inputDirectory:
+ source: MzMLToMzlite/dir
+ parallelismLevel:
+ default: 8
+ outputDirectory:
+ valueFrom: "output"
+ out: [dir1, dir2]"""
+
diff --git a/tests/TestingUtils/TestingUtils.fsproj b/tests/TestingUtils/TestingUtils.fsproj
index 5b0cacab..70dbba96 100644
--- a/tests/TestingUtils/TestingUtils.fsproj
+++ b/tests/TestingUtils/TestingUtils.fsproj
@@ -5,6 +5,14 @@
true
+
+
+
+
+
+
+
+