From 081ca0452bad26245525872637662e18bdb16bef Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 10:42:20 +0200 Subject: [PATCH 01/49] Add cwl model --- src/CWL/CWL.fs | 56 +++++++++++++++++++++++++++++++++++++++++ src/CWL/CWLTypes.fs | 37 +++++++++++++++++++++++++++ src/CWL/Inputs.fs | 18 +++++++++++++ src/CWL/Outputs.fs | 16 ++++++++++++ src/CWL/Requirements.fs | 51 +++++++++++++++++++++++++++++++++++++ 5 files changed, 178 insertions(+) create mode 100644 src/CWL/CWL.fs create mode 100644 src/CWL/CWLTypes.fs create mode 100644 src/CWL/Inputs.fs create mode 100644 src/CWL/Outputs.fs create mode 100644 src/CWL/Requirements.fs diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs new file mode 100644 index 00000000..1f12b369 --- /dev/null +++ b/src/CWL/CWL.fs @@ -0,0 +1,56 @@ +namespace ARCtrl.CWL + +open DynamicObj +open CWLTypes +open Requirements +open Inputs +open Outputs + +module CWL = + + type CWL ( + cwlVersion: string, + cls: Class, + outputs: Output [], + ?baseCommand: string [], + ?requirements: Requirement [], + ?hints: Requirement [], + ?inputs: Input [] + ) = + inherit DynamicObj () + + let mutable _cwlVersion: string = cwlVersion + let mutable _class: Class = cls + let mutable _outputs: Output [] = outputs + let mutable _baseCommand: string [] option = baseCommand + let mutable _requirements: Requirement [] option = requirements + let mutable _hints: Requirement [] option = hints + let mutable _inputs: Input [] option = inputs + + member this.CWLVersion + with get() = _cwlVersion + and set(version) = _cwlVersion <- version + + member this.Class + with get() = _class + and set(cls) = _class <- cls + + 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 \ No newline at end of file diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs new file mode 100644 index 00000000..fcfa5c74 --- /dev/null +++ b/src/CWL/CWLTypes.fs @@ -0,0 +1,37 @@ +namespace ARCtrl.CWL + +open DynamicObj + +module CWLTypes = + + 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 + } + + type CWLType = + | File of FileInstance + | Directory of DirectoryInstance + | Dirent of DirentInstance + | String + | Int + | Long + | Float + | Double + | Boolean + | Stdout + | Null + | Array of CWLType + + type Class = + | Workflow + | CommandLineTool + | ExpressionTool diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs new file mode 100644 index 00000000..9e03907d --- /dev/null +++ b/src/CWL/Inputs.fs @@ -0,0 +1,18 @@ +namespace ARCtrl.CWL + +open CWLTypes + +module Inputs = + + type InputBinding = { + Prefix: string option + Position: int option + ItemSeparator: string option + Separate: bool option + } + + type Input = { + Name: string + Type: CWLType + InputBinding: InputBinding option + } diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs new file mode 100644 index 00000000..f6240ec3 --- /dev/null +++ b/src/CWL/Outputs.fs @@ -0,0 +1,16 @@ +namespace ARCtrl.CWL + +open CWLTypes + +module Outputs = + + type OutputBinding = { + Glob: string option + } + + type Output = { + Name: string + Type: CWLType + OutputBinding: OutputBinding option + } + diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs new file mode 100644 index 00000000..f00a0929 --- /dev/null +++ b/src/CWL/Requirements.fs @@ -0,0 +1,51 @@ +namespace ARCtrl.CWL + +open DynamicObj +open CWLTypes + +module Requirements = + type DockerRequirement = { + DockerPull: string option + DockerFile: string option + DockerImageId: string option + } + + type InputRecordSchema () = + inherit DynamicObj () + + type InputEnumSchema () = + inherit DynamicObj () + + type InputArraySchema () = + inherit DynamicObj () + + type SchemaDefRequirementType = + | InputRecordSchema of InputRecordSchema + | InputEnumSchema of InputEnumSchema + | InputArraySchema of InputArraySchema + + type SoftwarePackage = { + Package: string + Version: string [] option + Specs: string [] option + } + + type EnvironmentDef = { + EnvName: string + EnvValue: string + } + + type ResourceRequirementInstance () = + inherit DynamicObj () + + type Requirement = + | InlineJavascriptRequirement + | SchemaDefRequirement of SchemaDefRequirementType [] + | DockerRequirement of DockerRequirement + | SoftwareRequirement of SoftwarePackage [] + | InitialWorkDirRequirement of CWLType [] + | EnvVarRequirement of EnvironmentDef + | ShellCommandRequirement + | ResourceRequirement of ResourceRequirementInstance + | NetworkAccessRequirement + From ef04da1849b3757d180446fcaa095fed2a32f8b3 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 10:42:38 +0200 Subject: [PATCH 02/49] Add cwl decode functions --- src/CWL/ARCtrl.CWL.fsproj | 24 +++++ src/CWL/Decode.fs | 185 ++++++++++++++++++++++++++++++++++++++ src/CWL/Encode.fs | 0 3 files changed, 209 insertions(+) create mode 100644 src/CWL/Decode.fs create mode 100644 src/CWL/Encode.fs diff --git a/src/CWL/ARCtrl.CWL.fsproj b/src/CWL/ARCtrl.CWL.fsproj index b709cf8c..6db6da17 100644 --- a/src/CWL/ARCtrl.CWL.fsproj +++ b/src/CWL/ARCtrl.CWL.fsproj @@ -8,6 +8,30 @@ + + + + + + + + + + + + + + + + nfdi4plants + ARC helper functions for Common workflow language. + MIT + logo.png + ARC F# FSharp dotnet .Net bioinformatics biology fable-library datascience dataplant nfdi metadata + https://github.com/nfdi4plants/ARCtrl/tree/main/src/CWL + https://github.com/nfdi4plants/ARCtrl + git + diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs new file mode 100644 index 00000000..5f494a0b --- /dev/null +++ b/src/CWL/Decode.fs @@ -0,0 +1,185 @@ +namespace ARCtrl.CWL + +open YAMLicious + +module Decode = + + let outputBindingGlobDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding) = + Decode.object (fun get -> + let glob = get.Optional.Field "glob" Decode.string + { Glob = glob } + ) + + let outputBindingDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding) = + Decode.object(fun get -> + let outputBinding = get.Required.Field "outputBinding" outputBindingGlobDecoder + outputBinding + ) + + 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 (Dirent { Entry = ""; Entryname = None; Writable = None }) + | "string" -> Array String + | "int" -> Array Int + | "long" -> Array Long + | "float" -> Array Float + | "double" -> Array Double + | "boolean" -> Array Boolean + | _ -> failwith "Invalid CWL type" + ) + + let cwlTypeDecoder: (YAMLiciousTypes.YAMLElement -> CWLType) = + 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 -> + match t with + | "File" -> File (FileInstance ()) + | "Directory" -> Directory (DirectoryInstance ()) + | "Dirent" -> Dirent { Entry = ""; Entryname = None; Writable = None } + | "string" -> String + | "int" -> Int + | "long" -> Long + | "float" -> Float + | "double" -> Double + | "boolean" -> Boolean + | "File[]" -> Array (File (FileInstance ())) + | "Directory[]" -> Array (Directory (DirectoryInstance ())) + | "Dirent[]" -> Array (Dirent { Entry = ""; Entryname = None; Writable = None }) + | "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" + | None -> + let cwlTypeArray = get.Required.Field "type" cwlArrayTypeDecoder + cwlTypeArray + ) + + let outputArrayDecoder: (YAMLiciousTypes.YAMLElement -> Output[]) = + Decode.object (fun get -> + let dict = get.Overflow.FieldList [] + [| + for key in dict.Keys do + let value = dict.[key] + let outputBinding = outputBindingDecoder value + let cwlType = cwlTypeDecoder value + { Name = key; Type = cwlType; OutputBinding = Some outputBinding } + |] + ) + + let outputsDecoder: (YAMLiciousTypes.YAMLElement -> Output[]) = + Decode.object (fun get -> + let outputs = get.Required.Field "outputs" outputArrayDecoder + outputs + ) + + let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = + Decode.array + ( + Decode.object (fun get -> + let cls = get.Required.Field "class" Decode.string + match cls with + | "InlineJavascriptRequirement" -> InlineJavascriptRequirement + | "SchemaDefRequirement" -> SchemaDefRequirement [||] + | "DockerRequirement" -> + let dockerReq = { + DockerPull = get.Optional.Field "dockerPull" Decode.string + DockerFile = Some "" + DockerImageId = get.Optional.Field "dockerImageId" Decode.string + } + DockerRequirement dockerReq + | "SoftwareRequirement" -> SoftwareRequirement [||] + | "InitialWorkDirRequirement" -> InitialWorkDirRequirement [||] + | "EnvVarRequirement" -> EnvVarRequirement {EnvName = ""; EnvValue = ""} + | "ShellCommandRequirement" -> ShellCommandRequirement + | "ResourceRequirement" -> ResourceRequirement (ResourceRequirementInstance()) + | "NetworkAccess" -> NetworkAccessRequirement + + ) + ) + + let requirementsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = + Decode.object (fun get -> + let requirements = get.Required.Field "requirements" requirementArrayDecoder + requirements + ) + + let hintsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = + Decode.object (fun get -> + let requirements = get.Required.Field "hints" requirementArrayDecoder + requirements + ) + + 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 + ) + + let inputArrayDecoder: (YAMLiciousTypes.YAMLElement -> Input[]) = + 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 = cwlTypeDecoder value + { Name = key; Type = cwlType; InputBinding = inputBinding } + |] + ) + + let inputsDecoder: (YAMLiciousTypes.YAMLElement -> Input[]) = + Decode.object (fun get -> + let outputs = get.Required.Field "inputs" inputArrayDecoder + outputs + ) + + let decodeAll = + let yamlCWL = Decode.read exampleCWL + CWL( + cwlVersion = Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) yamlCWL, + cls = + Decode.object (fun get -> + match get.Required.Field "class" Decode.string with + | "Workflow" -> Workflow + | "CommandLineTool" -> CommandLineTool + | "ExpressionTool" -> ExpressionTool + | _ -> failwith "Invalid class" + ) yamlCWL + , + outputs = outputsDecoder yamlCWL, + inputs = inputsDecoder yamlCWL, + //baseCommand = Decode.object (fun get -> get.Optional.Field "baseCommand" (Decode.array Decode.string)) yamlCWL, + requirements = requirementsDecoder yamlCWL, + hints = hintsDecoder yamlCWL + ) \ No newline at end of file diff --git a/src/CWL/Encode.fs b/src/CWL/Encode.fs new file mode 100644 index 00000000..e69de29b From 8cfb04c188f8b94db04968b7610f7bd886bce185 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 10:59:17 +0200 Subject: [PATCH 03/49] Add testing project --- ARCtrl.sln | 7 +++++++ src/CWL/Encode.fs | 1 + tests/CWL/ARCtrl.CWL.Tests.fsproj | 17 +++++++++++++++++ tests/CWL/Main.fs | 10 ++++++++++ 4 files changed, 35 insertions(+) create mode 100644 tests/CWL/ARCtrl.CWL.Tests.fsproj create mode 100644 tests/CWL/Main.fs 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/src/CWL/Encode.fs b/src/CWL/Encode.fs index e69de29b..4e267992 100644 --- a/src/CWL/Encode.fs +++ b/src/CWL/Encode.fs @@ -0,0 +1 @@ +namespace ARCtrl.CWL diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj new file mode 100644 index 00000000..ce2443a6 --- /dev/null +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -0,0 +1,17 @@ + + + + Exe + net8.0 + false + + + + + + + + + + + \ No newline at end of file diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs new file mode 100644 index 00000000..4b223fc7 --- /dev/null +++ b/tests/CWL/Main.fs @@ -0,0 +1,10 @@ +module ARCtrl.CWL.Tests + +open Fable.Pyxpecto + +let all = testSequenced <| testList "CWL" [ + () +] + +[] +let main argv = Pyxpecto.runTests [||] all From 63f8cdc84547ff3badb50667711a73179c2c3747 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 11:05:25 +0200 Subject: [PATCH 04/49] fix references in decode --- src/CWL/Decode.fs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 5f494a0b..73d19cac 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -1,6 +1,11 @@ namespace ARCtrl.CWL open YAMLicious +open YAMLicious.YAMLiciousTypes +open CWLTypes +open Requirements +open Inputs +open Outputs module Decode = @@ -164,9 +169,9 @@ module Decode = outputs ) - let decodeAll = - let yamlCWL = Decode.read exampleCWL - CWL( + let decodeAll (cwl: string) = + let yamlCWL = Decode.read cwl + CWL.CWL( cwlVersion = Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) yamlCWL, cls = Decode.object (fun get -> From cd3cdee21ef685366919aa537e796d33d927dbe0 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 11:08:38 +0200 Subject: [PATCH 05/49] fix cwl type naming --- src/CWL/CWL.fs | 2 +- src/CWL/Decode.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index 1f12b369..5b30c371 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -8,7 +8,7 @@ open Outputs module CWL = - type CWL ( + type CWLToolDescription ( cwlVersion: string, cls: Class, outputs: Output [], diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 73d19cac..8b543a13 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -171,7 +171,7 @@ module Decode = let decodeAll (cwl: string) = let yamlCWL = Decode.read cwl - CWL.CWL( + CWL.CWLToolDescription( cwlVersion = Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) yamlCWL, cls = Decode.object (fun get -> From 3da6974cea3440f17e7dacefb7c6ed79bf07e50c Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 11:12:51 +0200 Subject: [PATCH 06/49] change naming of CWL to CWLProcessingUnits --- src/CWL/CWL.fs | 2 +- src/CWL/Decode.fs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index 5b30c371..c118f4e5 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -6,7 +6,7 @@ open Requirements open Inputs open Outputs -module CWL = +module CWLProcessingUnits = type CWLToolDescription ( cwlVersion: string, diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 8b543a13..6092908f 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -171,7 +171,7 @@ module Decode = let decodeAll (cwl: string) = let yamlCWL = Decode.read cwl - CWL.CWLToolDescription( + CWLProcessingUnits.CWLToolDescription( cwlVersion = Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) yamlCWL, cls = Decode.object (fun get -> From 9c36d2d8b0baf5aa94f99a6960e5126ec56e9bec Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 11:15:34 +0200 Subject: [PATCH 07/49] fix test fsproj --- tests/CWL/ARCtrl.CWL.Tests.fsproj | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index ce2443a6..d39c55f7 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -6,8 +6,6 @@ false - - From 1932c44952c0cd818f15eddee7a0e186d3ded67e Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 19 Jul 2024 11:23:37 +0200 Subject: [PATCH 08/49] add test strings --- tests/CWL/ARCtrl.CWL.Tests.fsproj | 1 + tests/CWL/TestUtil.fs | 89 +++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 tests/CWL/TestUtil.fs diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index d39c55f7..129e0ae9 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -6,6 +6,7 @@ false + diff --git a/tests/CWL/TestUtil.fs b/tests/CWL/TestUtil.fs new file mode 100644 index 00000000..4c90383a --- /dev/null +++ b/tests/CWL/TestUtil.fs @@ -0,0 +1,89 @@ +module TestUtil + +let outputs ="""outputs: + output: + type: File + outputBinding: + glob: ./arc/runs/fsResult1/result.csv + example: + type: Directory + outputBinding: + glob: ./arc/runs/fsResult1/example.csv + exampleArray1: + type: File[] + outputBinding: + glob: ./arc/runs/fsResult1/example.csv + exampleArray2: + type: + type: array + items: File + outputBinding: + glob: ./arc/runs/fsResult1/example.csv""" + +let inputs ="""inputs: + arcDirectory: + type: Directory + firstArg: + type: File + inputBinding: + position: 1 + prefix: --example + secondArg: + type: string + inputBinding: + position: 2 + separate: false""" + +let requirements ="""requirements: + - class: DockerRequirement + dockerImageId: "devcontainer" + dockerFile: {$include: "FSharpArcCapsule/Dockerfile"} + - class: InitialWorkDirRequirement + listing: + - entryname: arc + entry: $(inputs.arcDirectory) + writable: true + - class: EnvVarRequirement + envDef: + - envName: DOTNET_NOLOGO + envValue: "true" + - class: NetworkAccess + networkAccess: true""" + +let cwl ="""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 From 6ae8e47c6e7ac2fa98425885249b4f0ab12d98ae Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 15 Sep 2024 16:59:05 +0200 Subject: [PATCH 09/49] Add cwl test --- src/ARCtrl/ARC.fs | 4 +- src/CWL/ARCtrl.CWL.fsproj | 3 +- src/CWL/Library.fs | 3 - tests/CWL/ARCtrl.CWL.Tests.fsproj | 1 + tests/CWL/CWLObject.Tests.fs | 157 ++++++++++++++++++++++++++++++ tests/CWL/Main.fs | 5 +- 6 files changed, 164 insertions(+), 9 deletions(-) delete mode 100644 src/CWL/Library.fs create mode 100644 tests/CWL/CWLObject.Tests.fs diff --git a/src/ARCtrl/ARC.fs b/src/ARCtrl/ARC.fs index b0bebd84..53b4826a 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 : CWL.CWLProcessingUnits.CWLToolDescription 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 : CWL.CWLProcessingUnits.CWLToolDescription, ?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 6db6da17..45139feb 100644 --- a/src/CWL/ARCtrl.CWL.fsproj +++ b/src/CWL/ARCtrl.CWL.fsproj @@ -15,14 +15,13 @@ - - + nfdi4plants 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/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index 129e0ae9..8e8894ab 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -7,6 +7,7 @@ + diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs new file mode 100644 index 00000000..6d545c7d --- /dev/null +++ b/tests/CWL/CWLObject.Tests.fs @@ -0,0 +1,157 @@ +module Tests.CWLObject + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs +open YAMLicious +open TestingUtils + + +let decodeOutput = + TestUtil.outputs + |> Decode.read + |> Decode.outputsDecoder + +let decodeInput = + TestUtil.inputs + |> Decode.read + |> Decode.inputsDecoder + +let decodeRequirement = + TestUtil.requirements + |> Decode.read + |> Decode.requirementsDecoder + +let decodeCWLToolDescription = + TestUtil.cwl + |> Decode.decodeAll + +let testOutput = + testList "outputs with basetypes and array" [ + testCase "Length" <| fun _ -> Expect.isTrue (4 = decodeOutput.Length) "Length of outputs is not 4" + testList "File" [ + let fileItem = decodeOutput.[0] + testCase "Name" <| fun _ -> Expect.isTrue ("output" = fileItem.Name) "Name of output is not 'output'" + testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of output is not File" + testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/result.csv"} = fileItem.OutputBinding) "OutputBinding of output is not Some Pattern" + ] + testList "Directory" [ + let directoryItem = decodeOutput.[1] + testCase "Name" <| fun _ -> Expect.isTrue ("example" = directoryItem.Name) "Name of output is not 'example'" + testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of output is not Directory" + testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/example.csv"} = directoryItem.OutputBinding) "OutputBinding of output is not Some Pattern" + ] + testList "File Array" [ + let fileArrayItem = decodeOutput.[2] + testCase "Name" <| fun _ -> Expect.isTrue ("exampleArray1" = fileArrayItem.Name) "Name of output is not 'exampleArray1'" + testCase "Type" <| fun _ -> Expect.isTrue ((Array (File (FileInstance()))) = fileArrayItem.Type) "Type of output is not Array File" + testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/example.csv"} = fileArrayItem.OutputBinding) "OutputBinding of output is not Some Pattern" + ] + testList "File Array 2" [ + let fileArrayItem = decodeOutput.[3] + testCase "Name" <| fun _ -> Expect.isTrue ("exampleArray2" = fileArrayItem.Name) "Name of output is not 'exampleArray2'" + testCase "Type" <| fun _ -> Expect.isTrue ((Array (File (FileInstance()))) = fileArrayItem.Type) "Type of output is not Array File" + testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/example.csv"} = fileArrayItem.OutputBinding) "OutputBinding of output is not Some Pattern" + ] + ] + +let testInput = + testList "inputs with Directory, File and string" [ + testCase "Length" <| fun _ -> Expect.isTrue (3 = decodeInput.Length) "Length of inputs is not 3" + testList "Directory" [ + let directoryItem = decodeInput.[0] + testCase "Name" <| fun _ -> Expect.isTrue ("arcDirectory" = directoryItem.Name) "Name of input is not 'arcDirectory'" + testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of input is not Directory" + ] + testList "File" [ + let fileItem = decodeInput.[1] + testCase "Name" <| fun _ -> Expect.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" + testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of input is not File" + testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = Some "--example"; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" + ] + testList "String" [ + let stringItem = decodeInput.[2] + testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" + testCase "Type" <| fun _ -> Expect.isTrue (String = stringItem.Type) "Type of input is not String" + testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = Some false} = stringItem.InputBinding) "InputBinding of input is not Some Pattern" + ] + ] + +let testRequirement = + testList "requirements with DockerRequirement, InitialWorkDirRequirement, EnvVarRequirement and NetworkAccess" [ + testCase "Length" <| fun _ -> Expect.isTrue (4 = decodeRequirement.Length) "Length of requirements is not 4" + testList "DockerRequirement" [ + let dockerItem = decodeRequirement.[0] + testCase "Class" <| fun _ -> Expect.isTrue (DockerRequirement {DockerPull = None; DockerFile = Some "FSharpArcCapsule/Dockerfile"; DockerImageId = Some "devcontainer"} = dockerItem) "Class of requirement is not DockerRequirement" + ] + testList "InitialWorkDirRequirement" [ + let initialWorkDirItem = decodeRequirement.[1] + testCase "Class" <| fun _ -> Expect.isTrue (InitialWorkDirRequirement [||] = initialWorkDirItem) "Class of requirement is not InitialWorkDirRequirement" + ] + testList "EnvVarRequirement" [ + let envVarItem = decodeRequirement.[2] + testCase "Class" <| fun _ -> Expect.isTrue (EnvVarRequirement {EnvName = ""; EnvValue = ""} = envVarItem) "Class of requirement is not EnvVarRequirement" + ] + testList "NetworkAccess" [ + let networkAccessItem = decodeRequirement.[3] + testCase "Class" <| fun _ -> Expect.isTrue (NetworkAccessRequirement = networkAccessItem) "Class of requirement is not NetworkAccess" + ] + ] + +let testCWLToolDescription = + testList "CWLToolDescription" [ + testCase "Class" <| fun _ -> Expect.isTrue (Class.CommandLineTool = decodeCWLToolDescription.Class) "Class of CWLToolDescription is not CommandLineTool" + testCase "CWLVersion" <| fun _ -> Expect.isTrue ("v1.2" = decodeCWLToolDescription.CWLVersion) "CWLVersion of CWLToolDescription is not v1.2" + testList "Hints" [ + let hintsItem = decodeCWLToolDescription.Hints + testCase "DockerRequirement" <| fun _ -> Expect.isTrue (DockerRequirement {DockerPull = Some "mcr.microsoft.com/dotnet/sdk:6.0"; DockerFile = None; DockerImageId = None} = hintsItem.Value.[0]) "Class of hint is not DockerRequirement" + ] + testList "Requirements" [ + let requirementsItem = decodeCWLToolDescription.Requirements + testCase "InitialWorkDirRequirement" <| fun _ -> Expect.isTrue (InitialWorkDirRequirement [||] = requirementsItem.Value.[0]) "Class of requirement is not InitialWorkDirRequirement" + testCase "EnvVarRequirement" <| fun _ -> Expect.isTrue (EnvVarRequirement {EnvName = ""; EnvValue = ""} = requirementsItem.Value.[1]) "Class of requirement is not EnvVarRequirement" + testCase "NetworkAccessRequirement" <| fun _ -> Expect.isTrue (NetworkAccessRequirement = requirementsItem.Value.[2]) "Class of requirement is not NetworkAccessRequirement" + ] + testList "Inputs" [ + let inputsItem = decodeCWLToolDescription.Inputs.Value + testCase "Length" <| fun _ -> Expect.isTrue (2 = inputsItem.Length) "Length of inputs is not 2" + testList "File" [ + let fileItem = inputsItem.[0] + testCase "Name" <| fun _ -> Expect.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" + testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of input is not File" + testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" + ] + testList "String" [ + let stringItem = inputsItem.[1] + testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" + testCase "Type" <| fun _ -> Expect.isTrue (String = stringItem.Type) "Type of input is not String" + testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} = stringItem.InputBinding) "InputBinding of input is not Some Pattern" + ] + ] + testList "Outputs" [ + let outputsItem = decodeCWLToolDescription.Outputs + testCase "Length" <| fun _ -> Expect.isTrue (2 = outputsItem.Length) "Length of outputs is not 2" + testList "Directory" [ + let directoryItem = outputsItem.[0] + testCase "Name" <| fun _ -> Expect.isTrue ("output" = directoryItem.Name) "Name of output is not 'output'" + testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of output is not Directory" + testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "$(runtime.outdir)/.nuget"} = directoryItem.OutputBinding) "OutputBinding of output is not Some Pattern" + ] + testList "File" [ + let fileItem = outputsItem.[1] + testCase "Name" <| fun _ -> Expect.isTrue ("output2" = fileItem.Name) "Name of output is not 'output2'" + testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of output is not File" + testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "$(runtime.outdir)/*.csv"} = fileItem.OutputBinding) "OutputBinding of output is not Some Pattern" + ] + ] + ] + +let main = + testList "CWL" [ + testOutput + testInput + testRequirement + testCWLToolDescription + ] \ No newline at end of file diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs index 4b223fc7..8c666591 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -1,9 +1,10 @@ -module ARCtrl.CWL.Tests +module CWL.Tests open Fable.Pyxpecto let all = testSequenced <| testList "CWL" [ - () + Tests.CWLObject.main + ] [] From bbc5a4218825e40454898a5ee574cc95460fc415 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 16 Sep 2024 17:49:04 +0200 Subject: [PATCH 10/49] fix optional fields --- src/CWL/Decode.fs | 70 +++++++++++++++++++++++++++++++---------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 6092908f..c1266641 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -121,15 +121,15 @@ module Decode = ) ) - let requirementsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = + let requirementsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[] option) = Decode.object (fun get -> - let requirements = get.Required.Field "requirements" requirementArrayDecoder + let requirements = get.Optional.Field "requirements" requirementArrayDecoder requirements ) - let hintsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = + let hintsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[] option) = Decode.object (fun get -> - let requirements = get.Required.Field "hints" requirementArrayDecoder + let requirements = get.Optional.Field "hints" requirementArrayDecoder requirements ) @@ -163,28 +163,50 @@ module Decode = |] ) - let inputsDecoder: (YAMLiciousTypes.YAMLElement -> Input[]) = + let inputsDecoder: (YAMLiciousTypes.YAMLElement -> Input[] option) = Decode.object (fun get -> - let outputs = get.Required.Field "inputs" inputArrayDecoder + let outputs = get.Optional.Field "inputs" inputArrayDecoder outputs ) - let decodeAll (cwl: string) = + let baseCommandDecoder: (YAMLiciousTypes.YAMLElement -> string [] option) = + Decode.object (fun get -> + get.Optional.Field "baseCommand" (Decode.array Decode.string) + ) + + let versionDecoder: (YAMLiciousTypes.YAMLElement -> string) = + Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) + + let classDecoder: (YAMLiciousTypes.YAMLElement -> Class) = + Decode.object (fun get -> + match get.Required.Field "class" Decode.string with + | "Workflow" -> Workflow + | "CommandLineTool" -> CommandLineTool + | "ExpressionTool" -> ExpressionTool + | _ -> failwith "Invalid class" + ) + + let decodeCommandLineTool (cwl: string) = let yamlCWL = Decode.read cwl - CWLProcessingUnits.CWLToolDescription( - cwlVersion = Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) yamlCWL, - cls = - Decode.object (fun get -> - match get.Required.Field "class" Decode.string with - | "Workflow" -> Workflow - | "CommandLineTool" -> CommandLineTool - | "ExpressionTool" -> ExpressionTool - | _ -> failwith "Invalid class" - ) yamlCWL - , - outputs = outputsDecoder yamlCWL, - inputs = inputsDecoder yamlCWL, - //baseCommand = Decode.object (fun get -> get.Optional.Field "baseCommand" (Decode.array Decode.string)) yamlCWL, - requirements = requirementsDecoder yamlCWL, - hints = hintsDecoder yamlCWL - ) \ No newline at end of file + 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 = + CWLProcessingUnits.CWLToolDescription( + cwlVersion, + CommandLineTool, + outputs + ) + 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 + description + From a9e64d3f94c697293d4ff898f08295b5ed2e52ce Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 16 Sep 2024 17:49:15 +0200 Subject: [PATCH 11/49] rewrite tests to be more clear --- tests/CWL/CWLObject.Tests.fs | 283 ++++++++++++++++++++++++++++++----- 1 file changed, 243 insertions(+), 40 deletions(-) diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 6d545c7d..f63d5247 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -18,42 +18,109 @@ let decodeInput = TestUtil.inputs |> Decode.read |> Decode.inputsDecoder + |>fun i ->i.Value let decodeRequirement = TestUtil.requirements |> Decode.read |> Decode.requirementsDecoder + |> fun r -> r.Value let decodeCWLToolDescription = TestUtil.cwl - |> Decode.decodeAll + |> Decode.decodeCommandLineTool let testOutput = testList "outputs with basetypes and array" [ - testCase "Length" <| fun _ -> Expect.isTrue (4 = decodeOutput.Length) "Length of outputs is not 4" + testCase "Length" <| fun _ -> + let expected = 4 + let actual = decodeOutput.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" testList "File" [ let fileItem = decodeOutput.[0] - testCase "Name" <| fun _ -> Expect.isTrue ("output" = fileItem.Name) "Name of output is not 'output'" - testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of output is not File" - testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/result.csv"} = fileItem.OutputBinding) "OutputBinding of output is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "output" + let actual = fileItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/result.csv"} + let actual = fileItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "Directory" [ let directoryItem = decodeOutput.[1] - testCase "Name" <| fun _ -> Expect.isTrue ("example" = directoryItem.Name) "Name of output is not 'example'" - testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of output is not Directory" - testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/example.csv"} = directoryItem.OutputBinding) "OutputBinding of output is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "example" + let actual = directoryItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Directory (DirectoryInstance()) + let actual = directoryItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} + let actual = directoryItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "File Array" [ let fileArrayItem = decodeOutput.[2] - testCase "Name" <| fun _ -> Expect.isTrue ("exampleArray1" = fileArrayItem.Name) "Name of output is not 'exampleArray1'" - testCase "Type" <| fun _ -> Expect.isTrue ((Array (File (FileInstance()))) = fileArrayItem.Type) "Type of output is not Array File" - testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/example.csv"} = fileArrayItem.OutputBinding) "OutputBinding of output is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "exampleArray1" + let actual = fileArrayItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Array (File (FileInstance())) + let actual = fileArrayItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} + let actual = fileArrayItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "File Array 2" [ let fileArrayItem = decodeOutput.[3] - testCase "Name" <| fun _ -> Expect.isTrue ("exampleArray2" = fileArrayItem.Name) "Name of output is not 'exampleArray2'" - testCase "Type" <| fun _ -> Expect.isTrue ((Array (File (FileInstance()))) = fileArrayItem.Type) "Type of output is not Array File" - testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "./arc/runs/fsResult1/example.csv"} = fileArrayItem.OutputBinding) "OutputBinding of output is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "exampleArray2" + let actual = fileArrayItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Array (File (FileInstance())) + let actual = fileArrayItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} + let actual = fileArrayItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] ] @@ -74,8 +141,18 @@ let testInput = testList "String" [ let stringItem = decodeInput.[2] testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" - testCase "Type" <| fun _ -> Expect.isTrue (String = stringItem.Type) "Type of input is not String" - testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = Some false} = stringItem.InputBinding) "InputBinding of input is not Some Pattern" + testCase "Type" <| fun _ -> + let expected = String + let actual = stringItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = Some false} + let actual = stringItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] ] @@ -84,66 +161,192 @@ let testRequirement = testCase "Length" <| fun _ -> Expect.isTrue (4 = decodeRequirement.Length) "Length of requirements is not 4" testList "DockerRequirement" [ let dockerItem = decodeRequirement.[0] - testCase "Class" <| fun _ -> Expect.isTrue (DockerRequirement {DockerPull = None; DockerFile = Some "FSharpArcCapsule/Dockerfile"; DockerImageId = Some "devcontainer"} = dockerItem) "Class of requirement is not DockerRequirement" + testCase "Class" <| fun _ -> + let expected = DockerRequirement {DockerPull = None; DockerFile = Some "FSharpArcCapsule/Dockerfile"; DockerImageId = Some "devcontainer"} + let actual = dockerItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "InitialWorkDirRequirement" [ let initialWorkDirItem = decodeRequirement.[1] - testCase "Class" <| fun _ -> Expect.isTrue (InitialWorkDirRequirement [||] = initialWorkDirItem) "Class of requirement is not InitialWorkDirRequirement" + testCase "Class" <| fun _ -> + let expected = InitialWorkDirRequirement [||] + let actual = initialWorkDirItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "EnvVarRequirement" [ let envVarItem = decodeRequirement.[2] - testCase "Class" <| fun _ -> Expect.isTrue (EnvVarRequirement {EnvName = ""; EnvValue = ""} = envVarItem) "Class of requirement is not EnvVarRequirement" + testCase "Class" <| fun _ -> + let expected = EnvVarRequirement {EnvName = ""; EnvValue = ""} + let actual = envVarItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "NetworkAccess" [ let networkAccessItem = decodeRequirement.[3] - testCase "Class" <| fun _ -> Expect.isTrue (NetworkAccessRequirement = networkAccessItem) "Class of requirement is not NetworkAccess" + testCase "Class" <| fun _ -> + let expected = NetworkAccessRequirement + let actual = networkAccessItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] ] let testCWLToolDescription = testList "CWLToolDescription" [ - testCase "Class" <| fun _ -> Expect.isTrue (Class.CommandLineTool = decodeCWLToolDescription.Class) "Class of CWLToolDescription is not CommandLineTool" - testCase "CWLVersion" <| fun _ -> Expect.isTrue ("v1.2" = decodeCWLToolDescription.CWLVersion) "CWLVersion of CWLToolDescription is not v1.2" + testCase "Class" <| fun _ -> + let expected = Class.CommandLineTool + let actual = decodeCWLToolDescription.Class + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "CWLVersion" <| fun _ -> + let expected = "v1.2" + let actual = decodeCWLToolDescription.CWLVersion + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "baseCommand" <| fun _ -> + let expected = Some [|"dotnet"; "fsi"; "script.fsx"|] + let actual = decodeCWLToolDescription.BaseCommand + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" testList "Hints" [ let hintsItem = decodeCWLToolDescription.Hints - testCase "DockerRequirement" <| fun _ -> Expect.isTrue (DockerRequirement {DockerPull = Some "mcr.microsoft.com/dotnet/sdk:6.0"; DockerFile = None; DockerImageId = None} = hintsItem.Value.[0]) "Class of hint is not DockerRequirement" + 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.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "Requirements" [ let requirementsItem = decodeCWLToolDescription.Requirements - testCase "InitialWorkDirRequirement" <| fun _ -> Expect.isTrue (InitialWorkDirRequirement [||] = requirementsItem.Value.[0]) "Class of requirement is not InitialWorkDirRequirement" - testCase "EnvVarRequirement" <| fun _ -> Expect.isTrue (EnvVarRequirement {EnvName = ""; EnvValue = ""} = requirementsItem.Value.[1]) "Class of requirement is not EnvVarRequirement" - testCase "NetworkAccessRequirement" <| fun _ -> Expect.isTrue (NetworkAccessRequirement = requirementsItem.Value.[2]) "Class of requirement is not NetworkAccessRequirement" + testCase "InitialWorkDirRequirement" <| fun _ -> + let expected = InitialWorkDirRequirement [||] + let actual = requirementsItem.Value.[0] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "EnvVarRequirement" <| fun _ -> + let expected = EnvVarRequirement {EnvName = ""; EnvValue = ""} + let actual = requirementsItem.Value.[1] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "NetworkAccessRequirement" <| fun _ -> + let expected = NetworkAccessRequirement + let actual = requirementsItem.Value.[2] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "Inputs" [ let inputsItem = decodeCWLToolDescription.Inputs.Value - testCase "Length" <| fun _ -> Expect.isTrue (2 = inputsItem.Length) "Length of inputs is not 2" + testCase "Length" <| fun _ -> + let expected = 2 + let actual = inputsItem.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" testList "File" [ let fileItem = inputsItem.[0] - testCase "Name" <| fun _ -> Expect.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" - testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of input is not File" - testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "firstArg" + let actual = fileItem.Name + Expect.isTrue + ("firstArg" = fileItem.Name) + "Name of input is not 'firstArg'" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} + let actual = fileItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "String" [ let stringItem = inputsItem.[1] - testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" - testCase "Type" <| fun _ -> Expect.isTrue (String = stringItem.Type) "Type of input is not String" - testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} = stringItem.InputBinding) "InputBinding of input is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "secondArg" + let actual = stringItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = String + let actual = stringItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} + let actual = stringItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] ] testList "Outputs" [ let outputsItem = decodeCWLToolDescription.Outputs - testCase "Length" <| fun _ -> Expect.isTrue (2 = outputsItem.Length) "Length of outputs is not 2" + testCase "Length" <| fun _ -> + let expected = 2 + let actual = outputsItem.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" testList "Directory" [ let directoryItem = outputsItem.[0] - testCase "Name" <| fun _ -> Expect.isTrue ("output" = directoryItem.Name) "Name of output is not 'output'" - testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of output is not Directory" - testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "$(runtime.outdir)/.nuget"} = directoryItem.OutputBinding) "OutputBinding of output is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "output" + let actual = directoryItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Directory (DirectoryInstance()) + let actual = directoryItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"} + let actual = directoryItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] testList "File" [ let fileItem = outputsItem.[1] - testCase "Name" <| fun _ -> Expect.isTrue ("output2" = fileItem.Name) "Name of output is not 'output2'" - testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of output is not File" - testCase "OutputBinding" <| fun _ -> Expect.isTrue (Some {Glob = Some "$(runtime.outdir)/*.csv"} = fileItem.OutputBinding) "OutputBinding of output is not Some Pattern" + testCase "Name" <| fun _ -> + let expected = "output2" + let actual = fileItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"} + let actual = fileItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] ] ] From a59d8346f2fe8981fd56780e60fce16db950024f Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 16 Sep 2024 18:25:56 +0200 Subject: [PATCH 12/49] reorganize tests --- tests/CWL/ARCtrl.CWL.Tests.fsproj | 4 +- tests/CWL/CWLObject.Tests.fs | 195 +----------------- tests/CWL/Inputs.Tests.fs | 47 +++++ tests/CWL/Main.fs | 6 +- tests/CWL/Outputs.Tests.fs | 108 ++++++++++ tests/CWL/Requirements.Tests.fs | 56 +++++ tests/CWL/TestUtil.fs | 89 -------- .../TestObjects.CWL/CommandLineTool.fs | 39 ++++ tests/TestingUtils/TestObjects.CWL/Inputs.fs | 15 ++ tests/TestingUtils/TestObjects.CWL/Outputs.fs | 21 ++ .../TestObjects.CWL/Requirements.fs | 17 ++ tests/TestingUtils/TestingUtils.fsproj | 4 + 12 files changed, 315 insertions(+), 286 deletions(-) create mode 100644 tests/CWL/Inputs.Tests.fs create mode 100644 tests/CWL/Outputs.Tests.fs create mode 100644 tests/CWL/Requirements.Tests.fs delete mode 100644 tests/CWL/TestUtil.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/Inputs.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/Outputs.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/Requirements.fs diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index 8e8894ab..5347a1c3 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -6,8 +6,10 @@ false - + + + diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index f63d5247..b7996c3a 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -8,195 +8,10 @@ open ARCtrl.CWL.Outputs open YAMLicious open TestingUtils - -let decodeOutput = - TestUtil.outputs - |> Decode.read - |> Decode.outputsDecoder - -let decodeInput = - TestUtil.inputs - |> Decode.read - |> Decode.inputsDecoder - |>fun i ->i.Value - -let decodeRequirement = - TestUtil.requirements - |> Decode.read - |> Decode.requirementsDecoder - |> fun r -> r.Value - let decodeCWLToolDescription = - TestUtil.cwl + TestObjects.CWL.CommandLineTool.cwl |> Decode.decodeCommandLineTool -let testOutput = - testList "outputs with basetypes and array" [ - testCase "Length" <| fun _ -> - let expected = 4 - let actual = decodeOutput.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testList "File" [ - let fileItem = decodeOutput.[0] - testCase "Name" <| fun _ -> - let expected = "output" - let actual = fileItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "Type" <| fun _ -> - let expected = File (FileInstance()) - let actual = fileItem.Type - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "OutputBinding" <| fun _ -> - let expected = Some {Glob = Some "./arc/runs/fsResult1/result.csv"} - let actual = fileItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "Directory" [ - let directoryItem = decodeOutput.[1] - testCase "Name" <| fun _ -> - let expected = "example" - let actual = directoryItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "Type" <| fun _ -> - let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "OutputBinding" <| fun _ -> - let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} - let actual = directoryItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "File Array" [ - let fileArrayItem = decodeOutput.[2] - testCase "Name" <| fun _ -> - let expected = "exampleArray1" - let actual = fileArrayItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "Type" <| fun _ -> - let expected = Array (File (FileInstance())) - let actual = fileArrayItem.Type - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "OutputBinding" <| fun _ -> - let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} - let actual = fileArrayItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "File Array 2" [ - let fileArrayItem = decodeOutput.[3] - testCase "Name" <| fun _ -> - let expected = "exampleArray2" - let actual = fileArrayItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "Type" <| fun _ -> - let expected = Array (File (FileInstance())) - let actual = fileArrayItem.Type - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "OutputBinding" <| fun _ -> - let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} - let actual = fileArrayItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - ] - -let testInput = - testList "inputs with Directory, File and string" [ - testCase "Length" <| fun _ -> Expect.isTrue (3 = decodeInput.Length) "Length of inputs is not 3" - testList "Directory" [ - let directoryItem = decodeInput.[0] - testCase "Name" <| fun _ -> Expect.isTrue ("arcDirectory" = directoryItem.Name) "Name of input is not 'arcDirectory'" - testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of input is not Directory" - ] - testList "File" [ - let fileItem = decodeInput.[1] - testCase "Name" <| fun _ -> Expect.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" - testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of input is not File" - testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = Some "--example"; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" - ] - testList "String" [ - let stringItem = decodeInput.[2] - testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" - testCase "Type" <| fun _ -> - let expected = String - let actual = stringItem.Type - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "InputBinding" <| fun _ -> - let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = Some false} - let actual = stringItem.InputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - ] - -let testRequirement = - testList "requirements with DockerRequirement, InitialWorkDirRequirement, EnvVarRequirement and NetworkAccess" [ - testCase "Length" <| fun _ -> Expect.isTrue (4 = decodeRequirement.Length) "Length of requirements is not 4" - testList "DockerRequirement" [ - let dockerItem = decodeRequirement.[0] - testCase "Class" <| fun _ -> - let expected = DockerRequirement {DockerPull = None; DockerFile = Some "FSharpArcCapsule/Dockerfile"; DockerImageId = Some "devcontainer"} - let actual = dockerItem - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "InitialWorkDirRequirement" [ - let initialWorkDirItem = decodeRequirement.[1] - testCase "Class" <| fun _ -> - let expected = InitialWorkDirRequirement [||] - let actual = initialWorkDirItem - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "EnvVarRequirement" [ - let envVarItem = decodeRequirement.[2] - testCase "Class" <| fun _ -> - let expected = EnvVarRequirement {EnvName = ""; EnvValue = ""} - let actual = envVarItem - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "NetworkAccess" [ - let networkAccessItem = decodeRequirement.[3] - testCase "Class" <| fun _ -> - let expected = NetworkAccessRequirement - let actual = networkAccessItem - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - ] - let testCWLToolDescription = testList "CWLToolDescription" [ testCase "Class" <| fun _ -> @@ -349,12 +164,4 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" ] ] - ] - -let main = - testList "CWL" [ - testOutput - testInput - testRequirement - testCWLToolDescription ] \ 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..c4643fff --- /dev/null +++ b/tests/CWL/Inputs.Tests.fs @@ -0,0 +1,47 @@ +module Tests.Inputs + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs +open YAMLicious +open TestingUtils + +let decodeInput = + TestObjects.CWL.Inputs.inputs + |> Decode.read + |> Decode.inputsDecoder + |>fun i ->i.Value + +let testInput = + testList "inputs with Directory, File and string" [ + testCase "Length" <| fun _ -> Expect.isTrue (3 = decodeInput.Length) "Length of inputs is not 3" + testList "Directory" [ + let directoryItem = decodeInput.[0] + testCase "Name" <| fun _ -> Expect.isTrue ("arcDirectory" = directoryItem.Name) "Name of input is not 'arcDirectory'" + testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of input is not Directory" + ] + testList "File" [ + let fileItem = decodeInput.[1] + testCase "Name" <| fun _ -> Expect.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" + testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of input is not File" + testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = Some "--example"; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" + ] + testList "String" [ + let stringItem = decodeInput.[2] + testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" + testCase "Type" <| fun _ -> + let expected = String + let actual = stringItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = Some false} + let actual = stringItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] \ No newline at end of file diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs index 8c666591..2e356660 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -3,8 +3,10 @@ module CWL.Tests open Fable.Pyxpecto let all = testSequenced <| testList "CWL" [ - Tests.CWLObject.main - + Tests.CWLObject.testCWLToolDescription + Tests.Outputs.testOutput + Tests.Inputs.testInput + Tests.Requirements.testRequirement ] [] diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs new file mode 100644 index 00000000..3b9a4e50 --- /dev/null +++ b/tests/CWL/Outputs.Tests.fs @@ -0,0 +1,108 @@ +module Tests.Outputs + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs +open YAMLicious +open TestingUtils + +let decodeOutput = + TestObjects.CWL.Outputs.outputs + |> Decode.read + |> Decode.outputsDecoder + +let testOutput = + testList "outputs with basetypes and array" [ + testCase "Length" <| fun _ -> + let expected = 4 + let actual = decodeOutput.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testList "File" [ + let fileItem = decodeOutput.[0] + testCase "Name" <| fun _ -> + let expected = "output" + let actual = fileItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/result.csv"} + let actual = fileItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "Directory" [ + let directoryItem = decodeOutput.[1] + testCase "Name" <| fun _ -> + let expected = "example" + let actual = directoryItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Directory (DirectoryInstance()) + let actual = directoryItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} + let actual = directoryItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "File Array" [ + let fileArrayItem = decodeOutput.[2] + testCase "Name" <| fun _ -> + let expected = "exampleArray1" + let actual = fileArrayItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Array (File (FileInstance())) + let actual = fileArrayItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} + let actual = fileArrayItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "File Array 2" [ + let fileArrayItem = decodeOutput.[3] + testCase "Name" <| fun _ -> + let expected = "exampleArray2" + let actual = fileArrayItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Array (File (FileInstance())) + let actual = fileArrayItem.Type + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} + let actual = fileArrayItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] \ 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..34b22bd5 --- /dev/null +++ b/tests/CWL/Requirements.Tests.fs @@ -0,0 +1,56 @@ +module Tests.Requirements + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs +open YAMLicious +open TestingUtils + +let decodeRequirement = + TestObjects.CWL.Requirements.requirements + |> Decode.read + |> Decode.requirementsDecoder + |> fun r -> r.Value + +let testRequirement = + testList "requirements with DockerRequirement, InitialWorkDirRequirement, EnvVarRequirement and NetworkAccess" [ + testCase "Length" <| fun _ -> Expect.isTrue (4 = decodeRequirement.Length) "Length of requirements is not 4" + testList "DockerRequirement" [ + let dockerItem = decodeRequirement.[0] + testCase "Class" <| fun _ -> + let expected = DockerRequirement {DockerPull = None; DockerFile = Some "FSharpArcCapsule/Dockerfile"; DockerImageId = Some "devcontainer"} + let actual = dockerItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "InitialWorkDirRequirement" [ + let initialWorkDirItem = decodeRequirement.[1] + testCase "Class" <| fun _ -> + let expected = InitialWorkDirRequirement [||] + let actual = initialWorkDirItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "EnvVarRequirement" [ + let envVarItem = decodeRequirement.[2] + testCase "Class" <| fun _ -> + let expected = EnvVarRequirement {EnvName = ""; EnvValue = ""} + let actual = envVarItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "NetworkAccess" [ + let networkAccessItem = decodeRequirement.[3] + testCase "Class" <| fun _ -> + let expected = NetworkAccessRequirement + let actual = networkAccessItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] \ No newline at end of file diff --git a/tests/CWL/TestUtil.fs b/tests/CWL/TestUtil.fs deleted file mode 100644 index 4c90383a..00000000 --- a/tests/CWL/TestUtil.fs +++ /dev/null @@ -1,89 +0,0 @@ -module TestUtil - -let outputs ="""outputs: - output: - type: File - outputBinding: - glob: ./arc/runs/fsResult1/result.csv - example: - type: Directory - outputBinding: - glob: ./arc/runs/fsResult1/example.csv - exampleArray1: - type: File[] - outputBinding: - glob: ./arc/runs/fsResult1/example.csv - exampleArray2: - type: - type: array - items: File - outputBinding: - glob: ./arc/runs/fsResult1/example.csv""" - -let inputs ="""inputs: - arcDirectory: - type: Directory - firstArg: - type: File - inputBinding: - position: 1 - prefix: --example - secondArg: - type: string - inputBinding: - position: 2 - separate: false""" - -let requirements ="""requirements: - - class: DockerRequirement - dockerImageId: "devcontainer" - dockerFile: {$include: "FSharpArcCapsule/Dockerfile"} - - class: InitialWorkDirRequirement - listing: - - entryname: arc - entry: $(inputs.arcDirectory) - writable: true - - class: EnvVarRequirement - envDef: - - envName: DOTNET_NOLOGO - envValue: "true" - - class: NetworkAccess - networkAccess: true""" - -let cwl ="""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/CommandLineTool.fs b/tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs new file mode 100644 index 00000000..ac2b6d0f --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs @@ -0,0 +1,39 @@ +module TestObjects.CWL.CommandLineTool + +let cwl ="""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/Inputs.fs b/tests/TestingUtils/TestObjects.CWL/Inputs.fs new file mode 100644 index 00000000..4e3bcb45 --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/Inputs.fs @@ -0,0 +1,15 @@ +module TestObjects.CWL.Inputs + +let inputs ="""inputs: + arcDirectory: + type: Directory + firstArg: + type: File + inputBinding: + position: 1 + prefix: --example + secondArg: + type: string + inputBinding: + position: 2 + separate: false""" \ 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..3a0b239e --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/Outputs.fs @@ -0,0 +1,21 @@ +module TestObjects.CWL.Outputs + +let outputs ="""outputs: + output: + type: File + outputBinding: + glob: ./arc/runs/fsResult1/result.csv + example: + type: Directory + outputBinding: + glob: ./arc/runs/fsResult1/example.csv + 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..75f39b13 --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/Requirements.fs @@ -0,0 +1,17 @@ +module TestObjects.CWL.Requirements + +let requirements ="""requirements: + - class: DockerRequirement + dockerImageId: "devcontainer" + dockerFile: {$include: "FSharpArcCapsule/Dockerfile"} + - class: InitialWorkDirRequirement + listing: + - entryname: arc + entry: $(inputs.arcDirectory) + writable: true + - class: EnvVarRequirement + envDef: + - envName: DOTNET_NOLOGO + envValue: "true" + - class: NetworkAccess + networkAccess: true""" \ No newline at end of file diff --git a/tests/TestingUtils/TestingUtils.fsproj b/tests/TestingUtils/TestingUtils.fsproj index 5b0cacab..9145651e 100644 --- a/tests/TestingUtils/TestingUtils.fsproj +++ b/tests/TestingUtils/TestingUtils.fsproj @@ -5,6 +5,10 @@ true + + + + From 3c5e2f8dfeb1f0cc352ab704b48b5bea2e13c41d Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 16 Sep 2024 19:13:09 +0200 Subject: [PATCH 13/49] dix dockerFile requirement --- src/CWL/Decode.fs | 17 +++++++++-------- src/CWL/Requirements.fs | 2 +- tests/CWL/Requirements.Tests.fs | 2 +- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index c1266641..fd2f2111 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -96,6 +96,14 @@ module Decode = outputs ) + 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 + let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = Decode.array ( @@ -104,20 +112,13 @@ module Decode = match cls with | "InlineJavascriptRequirement" -> InlineJavascriptRequirement | "SchemaDefRequirement" -> SchemaDefRequirement [||] - | "DockerRequirement" -> - let dockerReq = { - DockerPull = get.Optional.Field "dockerPull" Decode.string - DockerFile = Some "" - DockerImageId = get.Optional.Field "dockerImageId" Decode.string - } - DockerRequirement dockerReq + | "DockerRequirement" -> DockerRequirement (dockerRequirementDecoder get) | "SoftwareRequirement" -> SoftwareRequirement [||] | "InitialWorkDirRequirement" -> InitialWorkDirRequirement [||] | "EnvVarRequirement" -> EnvVarRequirement {EnvName = ""; EnvValue = ""} | "ShellCommandRequirement" -> ShellCommandRequirement | "ResourceRequirement" -> ResourceRequirement (ResourceRequirementInstance()) | "NetworkAccess" -> NetworkAccessRequirement - ) ) diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index f00a0929..caba2bb9 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -6,7 +6,7 @@ open CWLTypes module Requirements = type DockerRequirement = { DockerPull: string option - DockerFile: string option + DockerFile: Map option DockerImageId: string option } diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index 34b22bd5..7889cf6f 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -20,7 +20,7 @@ let testRequirement = testList "DockerRequirement" [ let dockerItem = decodeRequirement.[0] testCase "Class" <| fun _ -> - let expected = DockerRequirement {DockerPull = None; DockerFile = Some "FSharpArcCapsule/Dockerfile"; DockerImageId = Some "devcontainer"} + let expected = DockerRequirement {DockerPull = None; DockerFile = Some (Map [("$include", "FSharpArcCapsule/Dockerfile")]); DockerImageId = Some "devcontainer"} let actual = dockerItem Expect.isTrue (expected = actual) From 86913f6cdfa3cab39d552ee543ade062e4390557 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 17 Sep 2024 17:09:09 +0200 Subject: [PATCH 14/49] fix EnvVarRequirement --- src/CWL/Decode.fs | 20 ++++++++++++++++++- src/CWL/Requirements.fs | 2 +- tests/CWL/CWLObject.Tests.fs | 2 +- tests/CWL/Requirements.Tests.fs | 2 +- .../TestObjects.CWL/Requirements.fs | 2 ++ 5 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index fd2f2111..92e9ca57 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -104,6 +104,24 @@ module Decode = } dockerReq + let envVarRequirementDecoder (get: Decode.IGetters): EnvironmentDef[] = + let envDef = + get.Required.Field + "envDef" + ( + Decode.seq + ( + Decode.object (fun get2 -> + { + EnvName = get2.Required.Field "envName" Decode.string + EnvValue = get2.Required.Field "envValue" Decode.string + } + ) + ) + ) + envDef + |> Seq.toArray + let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = Decode.array ( @@ -115,7 +133,7 @@ module Decode = | "DockerRequirement" -> DockerRequirement (dockerRequirementDecoder get) | "SoftwareRequirement" -> SoftwareRequirement [||] | "InitialWorkDirRequirement" -> InitialWorkDirRequirement [||] - | "EnvVarRequirement" -> EnvVarRequirement {EnvName = ""; EnvValue = ""} + | "EnvVarRequirement" -> EnvVarRequirement (envVarRequirementDecoder get) | "ShellCommandRequirement" -> ShellCommandRequirement | "ResourceRequirement" -> ResourceRequirement (ResourceRequirementInstance()) | "NetworkAccess" -> NetworkAccessRequirement diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index caba2bb9..7d7a2033 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -44,7 +44,7 @@ module Requirements = | DockerRequirement of DockerRequirement | SoftwareRequirement of SoftwarePackage [] | InitialWorkDirRequirement of CWLType [] - | EnvVarRequirement of EnvironmentDef + | EnvVarRequirement of EnvironmentDef [] | ShellCommandRequirement | ResourceRequirement of ResourceRequirementInstance | NetworkAccessRequirement diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index b7996c3a..91d22a39 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -50,7 +50,7 @@ let testCWLToolDescription = (expected = actual) $"Expected: {expected}\nActual: {actual}" testCase "EnvVarRequirement" <| fun _ -> - let expected = EnvVarRequirement {EnvName = ""; EnvValue = ""} + let expected = EnvVarRequirement [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|] let actual = requirementsItem.Value.[1] Expect.isTrue (expected = actual) diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index 7889cf6f..764766e4 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -38,7 +38,7 @@ let testRequirement = testList "EnvVarRequirement" [ let envVarItem = decodeRequirement.[2] testCase "Class" <| fun _ -> - let expected = EnvVarRequirement {EnvName = ""; EnvValue = ""} + let expected = EnvVarRequirement [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}; {EnvName = "TEST"; EnvValue = "false"}|] let actual = envVarItem Expect.isTrue (expected = actual) diff --git a/tests/TestingUtils/TestObjects.CWL/Requirements.fs b/tests/TestingUtils/TestObjects.CWL/Requirements.fs index 75f39b13..5d9745fe 100644 --- a/tests/TestingUtils/TestObjects.CWL/Requirements.fs +++ b/tests/TestingUtils/TestObjects.CWL/Requirements.fs @@ -13,5 +13,7 @@ let requirements ="""requirements: envDef: - envName: DOTNET_NOLOGO envValue: "true" + - envName: TEST + envValue: "false" - class: NetworkAccess networkAccess: true""" \ No newline at end of file From 27c8037c21f05ebd887d67414fe1052d5ea940df Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 17 Sep 2024 18:46:17 +0200 Subject: [PATCH 15/49] fix SchemaDefRequirement --- src/CWL/Decode.fs | 16 +++++++++++++++- src/CWL/Requirements.fs | 8 ++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 92e9ca57..14d49857 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -122,6 +122,20 @@ module Decode = envDef |> Seq.toArray + let schemaDefRequirementDecoder (get: Decode.IGetters): SchemaDefRequirementType[] = + let schemaDef = + get.Required.Field + "types" + ( + Decode.seq + ( + Decode.map id Decode.string + ) + ) + |> Seq.map (fun m -> SchemaDefRequirementType(m.Keys |> Seq.item 0, m.Values |> Seq.item 0)) + schemaDef + |> Seq.toArray + let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = Decode.array ( @@ -129,7 +143,7 @@ module Decode = let cls = get.Required.Field "class" Decode.string match cls with | "InlineJavascriptRequirement" -> InlineJavascriptRequirement - | "SchemaDefRequirement" -> SchemaDefRequirement [||] + | "SchemaDefRequirement" -> SchemaDefRequirement (schemaDefRequirementDecoder get) | "DockerRequirement" -> DockerRequirement (dockerRequirementDecoder get) | "SoftwareRequirement" -> SoftwareRequirement [||] | "InitialWorkDirRequirement" -> InitialWorkDirRequirement [||] diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index 7d7a2033..9814d589 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -19,10 +19,10 @@ module Requirements = type InputArraySchema () = inherit DynamicObj () - type SchemaDefRequirementType = - | InputRecordSchema of InputRecordSchema - | InputEnumSchema of InputEnumSchema - | InputArraySchema of InputArraySchema + type SchemaDefRequirementType (types, definitions) as this = + inherit DynamicObj () + do + DynObj.setValue this (nameof types) definitions type SoftwarePackage = { Package: string From f4a662d4008df4ace3727a9f2b9134d3e1bdeab8 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 17 Sep 2024 19:14:28 +0200 Subject: [PATCH 16/49] fix SoftwareRequirements --- src/CWL/Decode.fs | 28 +++++++++++++++---- tests/CWL/Requirements.Tests.fs | 13 +++++++-- .../TestObjects.CWL/Requirements.fs | 5 ++++ 3 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 14d49857..1bebb4d7 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -109,7 +109,7 @@ module Decode = get.Required.Field "envDef" ( - Decode.seq + Decode.array ( Decode.object (fun get2 -> { @@ -120,21 +120,37 @@ module Decode = ) ) envDef - |> Seq.toArray + + let softwareRequirementDecoder (get: Decode.IGetters): SoftwarePackage[] = + let envDef = + get.Required.Field + "packages" + ( + Decode.array + ( + Decode.object (fun get2 -> + { + Package = get2.Required.Field "package" Decode.string + Version = get2.Optional.Field "version" (Decode.array Decode.string) + Specs = get2.Optional.Field "specs" (Decode.array Decode.string) + } + ) + ) + ) + envDef let schemaDefRequirementDecoder (get: Decode.IGetters): SchemaDefRequirementType[] = let schemaDef = get.Required.Field "types" ( - Decode.seq + Decode.array ( Decode.map id Decode.string ) ) - |> Seq.map (fun m -> SchemaDefRequirementType(m.Keys |> Seq.item 0, m.Values |> Seq.item 0)) + |> Array.map (fun m -> SchemaDefRequirementType(m.Keys |> Seq.item 0, m.Values |> Seq.item 0)) schemaDef - |> Seq.toArray let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = Decode.array @@ -145,7 +161,7 @@ module Decode = | "InlineJavascriptRequirement" -> InlineJavascriptRequirement | "SchemaDefRequirement" -> SchemaDefRequirement (schemaDefRequirementDecoder get) | "DockerRequirement" -> DockerRequirement (dockerRequirementDecoder get) - | "SoftwareRequirement" -> SoftwareRequirement [||] + | "SoftwareRequirement" -> SoftwareRequirement (softwareRequirementDecoder get) | "InitialWorkDirRequirement" -> InitialWorkDirRequirement [||] | "EnvVarRequirement" -> EnvVarRequirement (envVarRequirementDecoder get) | "ShellCommandRequirement" -> ShellCommandRequirement diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index 764766e4..752df657 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -16,7 +16,7 @@ let decodeRequirement = let testRequirement = testList "requirements with DockerRequirement, InitialWorkDirRequirement, EnvVarRequirement and NetworkAccess" [ - testCase "Length" <| fun _ -> Expect.isTrue (4 = decodeRequirement.Length) "Length of requirements is not 4" + testCase "Length" <| fun _ -> Expect.isTrue (5 = decodeRequirement.Length) "Length of requirements is not 5" testList "DockerRequirement" [ let dockerItem = decodeRequirement.[0] testCase "Class" <| fun _ -> @@ -44,8 +44,17 @@ let testRequirement = (expected = actual) $"Expected: {expected}\nActual: {actual}" ] + testList "SoftwareRequirement" [ + let softwareItem = decodeRequirement.[3] + testCase "Class" <| fun _ -> + let expected = SoftwareRequirement [|{Package = "interproscan"; Specs = Some [| "https://identifiers.org/rrid/RRID:SCR_005829" |]; Version = Some [| "5.21-60" |]}|] + let actual = softwareItem + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] testList "NetworkAccess" [ - let networkAccessItem = decodeRequirement.[3] + let networkAccessItem = decodeRequirement.[4] testCase "Class" <| fun _ -> let expected = NetworkAccessRequirement let actual = networkAccessItem diff --git a/tests/TestingUtils/TestObjects.CWL/Requirements.fs b/tests/TestingUtils/TestObjects.CWL/Requirements.fs index 5d9745fe..4d4312ed 100644 --- a/tests/TestingUtils/TestObjects.CWL/Requirements.fs +++ b/tests/TestingUtils/TestObjects.CWL/Requirements.fs @@ -15,5 +15,10 @@ let requirements ="""requirements: 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 From e44e736bd8d8345cbf706d927dc91f475817fc60 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 17 Sep 2024 20:30:46 +0200 Subject: [PATCH 17/49] fix workdirReq and resourceReq --- src/CWL/Decode.fs | 47 ++++++++++++++++++- src/CWL/Requirements.fs | 20 +++++++- tests/CWL/CWLObject.Tests.fs | 2 +- tests/CWL/Requirements.Tests.fs | 2 +- .../TestObjects.CWL/Requirements.fs | 2 + 5 files changed, 68 insertions(+), 5 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 1bebb4d7..39488741 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -139,6 +139,48 @@ module Decode = ) envDef + 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 + + let initialWorkDirRequirementDecoder (get: Decode.IGetters): CWLType[] = + let initialWorkDir = + try + get.Required.Field + "listing" + ( + Decode.array + ( + Decode.object (fun get2 -> + Dirent + { + // BUG: Entry Requires an Entryname to be present when it's an expression + Entry = get2.Required.Field "entry" decodeStringOrExpression + Entryname = get2.Optional.Field "entryname" decodeStringOrExpression + Writable = get2.Optional.Field "writable" Decode.bool + } + ) + ) + ) + with + | _ -> failwith "Only Dirent supported" + initialWorkDir + + 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 + ) + + let schemaDefRequirementDecoder (get: Decode.IGetters): SchemaDefRequirementType[] = let schemaDef = get.Required.Field @@ -152,6 +194,7 @@ module Decode = |> Array.map (fun m -> SchemaDefRequirementType(m.Keys |> Seq.item 0, m.Values |> Seq.item 0)) schemaDef + let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = Decode.array ( @@ -162,10 +205,10 @@ module Decode = | "SchemaDefRequirement" -> SchemaDefRequirement (schemaDefRequirementDecoder get) | "DockerRequirement" -> DockerRequirement (dockerRequirementDecoder get) | "SoftwareRequirement" -> SoftwareRequirement (softwareRequirementDecoder get) - | "InitialWorkDirRequirement" -> InitialWorkDirRequirement [||] + | "InitialWorkDirRequirement" -> InitialWorkDirRequirement (initialWorkDirRequirementDecoder get) | "EnvVarRequirement" -> EnvVarRequirement (envVarRequirementDecoder get) | "ShellCommandRequirement" -> ShellCommandRequirement - | "ResourceRequirement" -> ResourceRequirement (ResourceRequirementInstance()) + | "ResourceRequirement" -> ResourceRequirement (resourceRequirementDecoder get) | "NetworkAccess" -> NetworkAccessRequirement ) ) diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index 9814d589..5e72039f 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -35,8 +35,26 @@ module Requirements = EnvValue: string } - type ResourceRequirementInstance () = + type ResourceRequirementInstance ( + ?coresMin, + ?coresMax, + ?ramMin, + ?ramMax, + ?tmpdirMin, + ?tmpdirMax, + ?outdirMin, + ?outdirMax + ) as this = inherit DynamicObj () + do + DynObj.setValueOpt this (nameof coresMin) coresMin + DynObj.setValueOpt this (nameof coresMax) coresMax + DynObj.setValueOpt this (nameof ramMin) ramMin + DynObj.setValueOpt this (nameof ramMax) ramMax + DynObj.setValueOpt this (nameof tmpdirMin) tmpdirMin + DynObj.setValueOpt this (nameof tmpdirMax) tmpdirMax + DynObj.setValueOpt this (nameof outdirMin) outdirMin + DynObj.setValueOpt this (nameof outdirMax) outdirMax type Requirement = | InlineJavascriptRequirement diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 91d22a39..d677daab 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -44,7 +44,7 @@ let testCWLToolDescription = testList "Requirements" [ let requirementsItem = decodeCWLToolDescription.Requirements testCase "InitialWorkDirRequirement" <| fun _ -> - let expected = InitialWorkDirRequirement [||] + let expected = InitialWorkDirRequirement [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|] let actual = requirementsItem.Value.[0] Expect.isTrue (expected = actual) diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index 752df657..43352937 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -29,7 +29,7 @@ let testRequirement = testList "InitialWorkDirRequirement" [ let initialWorkDirItem = decodeRequirement.[1] testCase "Class" <| fun _ -> - let expected = InitialWorkDirRequirement [||] + let expected = InitialWorkDirRequirement [|Dirent {Entryname = Some "arc"; Entry = "$(inputs.arcDirectory)"; Writable = Some true}; Dirent {Entryname = None; Entry = "$(inputs.outputDirectory)"; Writable = Some true}|] let actual = initialWorkDirItem Expect.isTrue (expected = actual) diff --git a/tests/TestingUtils/TestObjects.CWL/Requirements.fs b/tests/TestingUtils/TestObjects.CWL/Requirements.fs index 4d4312ed..a909ada4 100644 --- a/tests/TestingUtils/TestObjects.CWL/Requirements.fs +++ b/tests/TestingUtils/TestObjects.CWL/Requirements.fs @@ -9,6 +9,8 @@ let requirements ="""requirements: - entryname: arc entry: $(inputs.arcDirectory) writable: true + - entry: $(inputs.outputDirectory) + writable: true - class: EnvVarRequirement envDef: - envName: DOTNET_NOLOGO From 3c6c0a3d88ea8104c29970ed6eeb6efcafd513b0 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 22 Sep 2024 16:04:26 +0200 Subject: [PATCH 18/49] start separating processunits for better modularity / edge case handling --- src/CWL/CWL.fs | 6 +- src/CWL/Decode.fs | 6 +- src/CWL/Inputs.fs | 24 +++---- src/CWL/Outputs.fs | 18 +++--- src/CWL/Requirements.fs | 111 ++++++++++++++++---------------- tests/CWL/CWLObject.Tests.fs | 6 +- tests/CWL/Inputs.Tests.fs | 4 +- tests/CWL/Outputs.Tests.fs | 4 +- tests/CWL/Requirements.Tests.fs | 4 +- 9 files changed, 92 insertions(+), 91 deletions(-) diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index c118f4e5..25fc70bd 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -2,9 +2,9 @@ namespace ARCtrl.CWL open DynamicObj open CWLTypes -open Requirements -open Inputs -open Outputs +open Requirements.CommandLineTool +open Inputs.CommandLineTool +open Outputs.CommandLineTool module CWLProcessingUnits = diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 39488741..4e5e0bce 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -3,9 +3,9 @@ namespace ARCtrl.CWL open YAMLicious open YAMLicious.YAMLiciousTypes open CWLTypes -open Requirements -open Inputs -open Outputs +open Requirements.CommandLineTool +open Inputs.CommandLineTool +open Outputs.CommandLineTool module Decode = diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 9e03907d..e201accd 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -4,15 +4,17 @@ open CWLTypes module Inputs = - type InputBinding = { - Prefix: string option - Position: int option - ItemSeparator: string option - Separate: bool option - } + module CommandLineTool = + + type InputBinding = { + Prefix: string option + Position: int option + ItemSeparator: string option + Separate: bool option + } - type Input = { - Name: string - Type: CWLType - InputBinding: InputBinding option - } + type Input = { + Name: string + Type: CWLType + InputBinding: InputBinding option + } diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index f6240ec3..5a60b442 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -4,13 +4,15 @@ open CWLTypes module Outputs = - type OutputBinding = { - Glob: string option - } + module CommandLineTool = - type Output = { - Name: string - Type: CWLType - OutputBinding: OutputBinding option - } + type OutputBinding = { + Glob: string option + } + + type Output = { + Name: string + Type: CWLType + OutputBinding: OutputBinding option + } diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index 5e72039f..c13ec9a4 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -4,66 +4,69 @@ open DynamicObj open CWLTypes module Requirements = - type DockerRequirement = { - DockerPull: string option - DockerFile: Map option - DockerImageId: string option - } - type InputRecordSchema () = - inherit DynamicObj () + module CommandLineTool = - type InputEnumSchema () = - inherit DynamicObj () + type DockerRequirement = { + DockerPull: string option + DockerFile: Map option + DockerImageId: string option + } - type InputArraySchema () = - inherit DynamicObj () + type InputRecordSchema () = + inherit DynamicObj () - type SchemaDefRequirementType (types, definitions) as this = - inherit DynamicObj () - do - DynObj.setValue this (nameof types) definitions + type InputEnumSchema () = + inherit DynamicObj () - type SoftwarePackage = { - Package: string - Version: string [] option - Specs: string [] option - } + type InputArraySchema () = + inherit DynamicObj () - type EnvironmentDef = { - EnvName: string - EnvValue: string - } + type SchemaDefRequirementType (types, definitions) as this = + inherit DynamicObj () + do + DynObj.setValue this (nameof types) definitions - type ResourceRequirementInstance ( - ?coresMin, - ?coresMax, - ?ramMin, - ?ramMax, - ?tmpdirMin, - ?tmpdirMax, - ?outdirMin, - ?outdirMax - ) as this = - inherit DynamicObj () - do - DynObj.setValueOpt this (nameof coresMin) coresMin - DynObj.setValueOpt this (nameof coresMax) coresMax - DynObj.setValueOpt this (nameof ramMin) ramMin - DynObj.setValueOpt this (nameof ramMax) ramMax - DynObj.setValueOpt this (nameof tmpdirMin) tmpdirMin - DynObj.setValueOpt this (nameof tmpdirMax) tmpdirMax - DynObj.setValueOpt this (nameof outdirMin) outdirMin - DynObj.setValueOpt this (nameof outdirMax) outdirMax + type SoftwarePackage = { + Package: string + Version: string [] option + Specs: string [] option + } - type Requirement = - | InlineJavascriptRequirement - | SchemaDefRequirement of SchemaDefRequirementType [] - | DockerRequirement of DockerRequirement - | SoftwareRequirement of SoftwarePackage [] - | InitialWorkDirRequirement of CWLType [] - | EnvVarRequirement of EnvironmentDef [] - | ShellCommandRequirement - | ResourceRequirement of ResourceRequirementInstance - | NetworkAccessRequirement + type EnvironmentDef = { + EnvName: string + EnvValue: string + } + + type ResourceRequirementInstance ( + ?coresMin, + ?coresMax, + ?ramMin, + ?ramMax, + ?tmpdirMin, + ?tmpdirMax, + ?outdirMin, + ?outdirMax + ) as this = + inherit DynamicObj () + do + DynObj.setValueOpt this (nameof coresMin) coresMin + DynObj.setValueOpt this (nameof coresMax) coresMax + DynObj.setValueOpt this (nameof ramMin) ramMin + DynObj.setValueOpt this (nameof ramMax) ramMax + DynObj.setValueOpt this (nameof tmpdirMin) tmpdirMin + DynObj.setValueOpt this (nameof tmpdirMax) tmpdirMax + DynObj.setValueOpt this (nameof outdirMin) outdirMin + DynObj.setValueOpt this (nameof outdirMax) outdirMax + + type Requirement = + | InlineJavascriptRequirement + | SchemaDefRequirement of SchemaDefRequirementType [] + | DockerRequirement of DockerRequirement + | SoftwareRequirement of SoftwarePackage [] + | InitialWorkDirRequirement of CWLType [] + | EnvVarRequirement of EnvironmentDef [] + | ShellCommandRequirement + | ResourceRequirement of ResourceRequirementInstance + | NetworkAccessRequirement diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index d677daab..698779e3 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -2,9 +2,9 @@ module Tests.CWLObject open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs +open ARCtrl.CWL.Requirements.CommandLineTool +open ARCtrl.CWL.Inputs.CommandLineTool +open ARCtrl.CWL.Outputs.CommandLineTool open YAMLicious open TestingUtils diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs index c4643fff..b0b2cda1 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -2,9 +2,7 @@ module Tests.Inputs open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs +open ARCtrl.CWL.Inputs.CommandLineTool open YAMLicious open TestingUtils diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs index 3b9a4e50..0ba00da2 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -2,9 +2,7 @@ module Tests.Outputs open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs +open ARCtrl.CWL.Outputs.CommandLineTool open YAMLicious open TestingUtils diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index 43352937..def11c91 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -2,9 +2,7 @@ module Tests.Requirements open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs +open ARCtrl.CWL.Requirements.CommandLineTool open YAMLicious open TestingUtils From 13d8a9c75178e4d5427608002e06e7c3e0dfdaf5 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 22 Sep 2024 19:28:20 +0200 Subject: [PATCH 19/49] reorder and specify wf specific types --- src/CWL/ARCtrl.CWL.fsproj | 2 +- src/CWL/CWL.fs | 6 +- src/CWL/CWLTypes.fs | 20 ++++++ src/CWL/Decode.fs | 6 +- src/CWL/Inputs.fs | 33 ++++++---- src/CWL/Outputs.fs | 21 +++--- src/CWL/Requirements.fs | 111 ++++++++++++++------------------ tests/CWL/CWLObject.Tests.fs | 6 +- tests/CWL/Inputs.Tests.fs | 2 +- tests/CWL/Outputs.Tests.fs | 2 +- tests/CWL/Requirements.Tests.fs | 2 +- 11 files changed, 113 insertions(+), 98 deletions(-) diff --git a/src/CWL/ARCtrl.CWL.fsproj b/src/CWL/ARCtrl.CWL.fsproj index 45139feb..dbde7a02 100644 --- a/src/CWL/ARCtrl.CWL.fsproj +++ b/src/CWL/ARCtrl.CWL.fsproj @@ -10,8 +10,8 @@ - + diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index 25fc70bd..c118f4e5 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -2,9 +2,9 @@ namespace ARCtrl.CWL open DynamicObj open CWLTypes -open Requirements.CommandLineTool -open Inputs.CommandLineTool -open Outputs.CommandLineTool +open Requirements +open Inputs +open Outputs module CWLProcessingUnits = diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs index fcfa5c74..b3164fc4 100644 --- a/src/CWL/CWLTypes.fs +++ b/src/CWL/CWLTypes.fs @@ -35,3 +35,23 @@ module CWLTypes = | Workflow | CommandLineTool | ExpressionTool + + type InputRecordSchema () = + inherit DynamicObj () + + type InputEnumSchema () = + inherit DynamicObj () + + type InputArraySchema () = + inherit DynamicObj () + + type SchemaDefRequirementType (types, definitions) as this = + inherit DynamicObj () + do + DynObj.setValue this (nameof types) definitions + + type SoftwarePackage = { + Package: string + Version: string [] option + Specs: string [] option + } diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 4e5e0bce..39488741 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -3,9 +3,9 @@ namespace ARCtrl.CWL open YAMLicious open YAMLicious.YAMLiciousTypes open CWLTypes -open Requirements.CommandLineTool -open Inputs.CommandLineTool -open Outputs.CommandLineTool +open Requirements +open Inputs +open Outputs module Decode = diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index e201accd..0590695b 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -1,20 +1,29 @@ namespace ARCtrl.CWL open CWLTypes +open Outputs.Workflow module Inputs = - module CommandLineTool = - - type InputBinding = { - Prefix: string option - Position: int option - ItemSeparator: string option - Separate: bool option - } + type InputBinding = { + Prefix: string option + Position: int option + ItemSeparator: string option + Separate: bool option + } - type Input = { - Name: string - Type: CWLType - InputBinding: InputBinding option + type Input = { + Name: string + Type: CWLType + InputBinding: InputBinding option + } + + module Workflow = + + type StepInput = { + Id: string + Source: StepOutput option + linkMerge: string option + defaultValue: string option + valueFrom: string option } diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index 5a60b442..a85f6a39 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -4,15 +4,18 @@ open CWLTypes module Outputs = - module CommandLineTool = + type OutputBinding = { + Glob: string option + } - type OutputBinding = { - Glob: string option - } + type Output = { + Name: string + Type: CWLType + OutputBinding: OutputBinding option + } - type Output = { - Name: string - Type: CWLType - OutputBinding: OutputBinding option - } + module Workflow = + type StepOutput = { + Id: string + } \ No newline at end of file diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index c13ec9a4..93ba0c38 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -5,68 +5,51 @@ open CWLTypes module Requirements = - module CommandLineTool = - - type DockerRequirement = { - DockerPull: string option - DockerFile: Map option - DockerImageId: string option - } - - type InputRecordSchema () = - inherit DynamicObj () - - type InputEnumSchema () = - inherit DynamicObj () - - type InputArraySchema () = - inherit DynamicObj () - - type SchemaDefRequirementType (types, definitions) as this = - inherit DynamicObj () - do - DynObj.setValue this (nameof types) definitions - - type SoftwarePackage = { - Package: string - Version: string [] option - Specs: string [] option - } - - type EnvironmentDef = { - EnvName: string - EnvValue: string - } - - type ResourceRequirementInstance ( - ?coresMin, - ?coresMax, - ?ramMin, - ?ramMax, - ?tmpdirMin, - ?tmpdirMax, - ?outdirMin, - ?outdirMax - ) as this = - inherit DynamicObj () - do - DynObj.setValueOpt this (nameof coresMin) coresMin - DynObj.setValueOpt this (nameof coresMax) coresMax - DynObj.setValueOpt this (nameof ramMin) ramMin - DynObj.setValueOpt this (nameof ramMax) ramMax - DynObj.setValueOpt this (nameof tmpdirMin) tmpdirMin - DynObj.setValueOpt this (nameof tmpdirMax) tmpdirMax - DynObj.setValueOpt this (nameof outdirMin) outdirMin - DynObj.setValueOpt this (nameof outdirMax) outdirMax - - type Requirement = - | InlineJavascriptRequirement - | SchemaDefRequirement of SchemaDefRequirementType [] - | DockerRequirement of DockerRequirement - | SoftwareRequirement of SoftwarePackage [] - | InitialWorkDirRequirement of CWLType [] - | EnvVarRequirement of EnvironmentDef [] - | ShellCommandRequirement - | ResourceRequirement of ResourceRequirementInstance - | NetworkAccessRequirement + type DockerRequirement = { + DockerPull: string option + DockerFile: Map option + DockerImageId: string option + } + + + type EnvironmentDef = { + EnvName: string + EnvValue: string + } + + type ResourceRequirementInstance ( + ?coresMin, + ?coresMax, + ?ramMin, + ?ramMax, + ?tmpdirMin, + ?tmpdirMax, + ?outdirMin, + ?outdirMax + ) as this = + inherit DynamicObj () + do + DynObj.setValueOpt this (nameof coresMin) coresMin + DynObj.setValueOpt this (nameof coresMax) coresMax + DynObj.setValueOpt this (nameof ramMin) ramMin + DynObj.setValueOpt this (nameof ramMax) ramMax + DynObj.setValueOpt this (nameof tmpdirMin) tmpdirMin + DynObj.setValueOpt this (nameof tmpdirMax) tmpdirMax + DynObj.setValueOpt this (nameof outdirMin) outdirMin + DynObj.setValueOpt this (nameof outdirMax) outdirMax + + type Requirement = + | InlineJavascriptRequirement + | SchemaDefRequirement of SchemaDefRequirementType [] + | DockerRequirement of DockerRequirement + | SoftwareRequirement of SoftwarePackage [] + | InitialWorkDirRequirement of CWLType [] + | EnvVarRequirement of EnvironmentDef [] + | ShellCommandRequirement + | ResourceRequirement of ResourceRequirementInstance + | NetworkAccessRequirement + | SubworkflowFeatureRequirement + | ScatterFeatureRequirement + | MultipleInputFeatureRequirement + | StepInputExpressionRequirement diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 698779e3..d677daab 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -2,9 +2,9 @@ module Tests.CWLObject open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements.CommandLineTool -open ARCtrl.CWL.Inputs.CommandLineTool -open ARCtrl.CWL.Outputs.CommandLineTool +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs open YAMLicious open TestingUtils diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs index b0b2cda1..311af5ed 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -2,7 +2,7 @@ module Tests.Inputs open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Inputs.CommandLineTool +open ARCtrl.CWL.Inputs open YAMLicious open TestingUtils diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs index 0ba00da2..b7f02574 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -2,7 +2,7 @@ module Tests.Outputs open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Outputs.CommandLineTool +open ARCtrl.CWL.Outputs open YAMLicious open TestingUtils diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index def11c91..f2ec2731 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -2,7 +2,7 @@ module Tests.Requirements open ARCtrl.CWL open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements.CommandLineTool +open ARCtrl.CWL.Requirements open YAMLicious open TestingUtils From c7a00821ed9ad34deabe358bef05c5d61f6cc277 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 22 Sep 2024 19:50:00 +0200 Subject: [PATCH 20/49] change in-/outputs to dynamicObj for more flexibility --- src/CWL/Decode.fs | 16 ++++++++++++++-- src/CWL/Inputs.fs | 20 ++++++++++++++------ src/CWL/Outputs.fs | 18 +++++++++++++----- tests/CWL/CWLObject.Tests.fs | 8 ++++---- tests/CWL/Inputs.Tests.fs | 6 +++--- tests/CWL/Outputs.Tests.fs | 8 ++++---- 6 files changed, 52 insertions(+), 24 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 39488741..a38c96bd 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -6,6 +6,7 @@ open CWLTypes open Requirements open Inputs open Outputs +open DynamicObj module Decode = @@ -86,7 +87,11 @@ module Decode = let value = dict.[key] let outputBinding = outputBindingDecoder value let cwlType = cwlTypeDecoder value - { Name = key; Type = cwlType; OutputBinding = Some outputBinding } + Output( + key, + cwlType, + outputBinding + ) |] ) @@ -251,7 +256,14 @@ module Decode = let value = dict.[key] let inputBinding = inputBindingDecoder value let cwlType = cwlTypeDecoder value - { Name = key; Type = cwlType; InputBinding = inputBinding } + let input = + Input( + key, + cwlType + ) + if inputBinding.IsSome then + DynObj.setValueOpt input "inputBinding" inputBinding + input |] ) diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 0590695b..4bfd547d 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -2,6 +2,7 @@ namespace ARCtrl.CWL open CWLTypes open Outputs.Workflow +open DynamicObj module Inputs = @@ -11,12 +12,19 @@ module Inputs = ItemSeparator: string option Separate: bool option } - - type Input = { - Name: string - Type: CWLType - InputBinding: InputBinding option - } + + type Input ( + name: string, + ?type_: CWLType, + ?inputBinding: InputBinding + ) as this = + inherit DynamicObj () + do + DynObj.setValueOpt this ("type") type_ + DynObj.setValueOpt this ("inputBinding") inputBinding + member this.Name = name + member this.Type = DynObj.tryGetTypedValue ("type") this + member this.InputBinding = DynObj.tryGetTypedValue ("inputBinding") this module Workflow = diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index a85f6a39..44bca1e8 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -1,6 +1,7 @@ namespace ARCtrl.CWL open CWLTypes +open DynamicObj module Outputs = @@ -8,11 +9,18 @@ module Outputs = Glob: string option } - type Output = { - Name: string - Type: CWLType - OutputBinding: OutputBinding option - } + type Output ( + name: string, + ?type_: CWLType, + ?outputBinding: OutputBinding + ) as this = + inherit DynamicObj () + do + DynObj.setValueOpt this ("type") type_ + DynObj.setValueOpt this ("outputBinding") outputBinding + member this.Name = name + member this.Type = DynObj.tryGetTypedValue ("type") this + member this.OutputBinding = DynObj.tryGetTypedValue ("outputBinding") this module Workflow = diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index d677daab..f9331d34 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -80,7 +80,7 @@ let testCWLToolDescription = "Name of input is not 'firstArg'" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type + let actual = fileItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -101,7 +101,7 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = String - let actual = stringItem.Type + let actual = stringItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -131,7 +131,7 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type + let actual = directoryItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -152,7 +152,7 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type + let actual = fileItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs index 311af5ed..8a7aabb1 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -18,12 +18,12 @@ let testInput = testList "Directory" [ let directoryItem = decodeInput.[0] testCase "Name" <| fun _ -> Expect.isTrue ("arcDirectory" = directoryItem.Name) "Name of input is not 'arcDirectory'" - testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type) "Type of input is not Directory" + testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type.Value) "Type of input is not Directory" ] testList "File" [ let fileItem = decodeInput.[1] testCase "Name" <| fun _ -> Expect.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" - testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type) "Type of input is not File" + testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type.Value) "Type of input is not File" testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = Some "--example"; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" ] testList "String" [ @@ -31,7 +31,7 @@ let testInput = testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" testCase "Type" <| fun _ -> let expected = String - let actual = stringItem.Type + let actual = stringItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs index b7f02574..b28c3d81 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -29,7 +29,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type + let actual = fileItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -50,7 +50,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type + let actual = directoryItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -71,7 +71,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Array (File (FileInstance())) - let actual = fileArrayItem.Type + let actual = fileArrayItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -92,7 +92,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Array (File (FileInstance())) - let actual = fileArrayItem.Type + let actual = fileArrayItem.Type.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" From 993023849ef4e1fec616b059468b836e917a0cb8 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 17:17:50 +0200 Subject: [PATCH 21/49] add workflow steps --- src/CWL/ARCtrl.CWL.fsproj | 1 + src/CWL/Inputs.fs | 3 +-- src/CWL/WorkflowSteps.fs | 44 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 src/CWL/WorkflowSteps.fs diff --git a/src/CWL/ARCtrl.CWL.fsproj b/src/CWL/ARCtrl.CWL.fsproj index dbde7a02..655ca106 100644 --- a/src/CWL/ARCtrl.CWL.fsproj +++ b/src/CWL/ARCtrl.CWL.fsproj @@ -12,6 +12,7 @@ + diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 4bfd547d..a7666685 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -30,8 +30,7 @@ module Inputs = type StepInput = { Id: string - Source: StepOutput option - linkMerge: string option + Source: string option defaultValue: string option valueFrom: string option } diff --git a/src/CWL/WorkflowSteps.fs b/src/CWL/WorkflowSteps.fs new file mode 100644 index 00000000..6b8945d5 --- /dev/null +++ b/src/CWL/WorkflowSteps.fs @@ -0,0 +1,44 @@ +namespace ARCtrl.CWL + +open CWLTypes +open Outputs.Workflow +open Inputs.Workflow +open Requirements +open DynamicObj + +module WorkflowSteps = + + type WorkflowStep ( + in_: StepInput [], + out_: StepOutput [], + run: string, + ?requirements: Requirement [], + ?hints: Requirement [] + ) = + inherit DynamicObj () + + let mutable _in: StepInput [] = in_ + let mutable _out: StepOutput [] = out_ + let mutable _run: string = run + let mutable _requirements: Requirement [] option = requirements + let mutable _hints: Requirement [] option = hints + + 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 From 0d049b16aeb5acafc412aeb804f35332e49694a8 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 17:18:12 +0200 Subject: [PATCH 22/49] add workflow description type --- src/CWL/CWL.fs | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index c118f4e5..21b32e13 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -5,6 +5,7 @@ open CWLTypes open Requirements open Inputs open Outputs +open WorkflowSteps module CWLProcessingUnits = @@ -53,4 +54,51 @@ module CWLProcessingUnits = member this.Inputs with get() = _inputs - and set(inputs) = _inputs <- inputs \ No newline at end of file + and set(inputs) = _inputs <- inputs + + type CWLWorkflowDescription( + cwlVersion: string, + cls: Class, + steps: WorkflowStep [], + inputs: Input [], + outputs: Output [], + ?requirements: Requirement [], + ?hints: Requirement [] + ) = + inherit DynamicObj() + + let mutable _cwlVersion: string = cwlVersion + let mutable _class: Class = cls + let mutable _steps: WorkflowStep [] = steps + let mutable _inputs: Input [] = inputs + let mutable _outputs: Output [] = outputs + let mutable _requirements: Requirement [] option = requirements + let mutable _hints: Requirement [] option = hints + + member this.CWLVersion + with get() = _cwlVersion + and set(version) = _cwlVersion <- version + + member this.Class + with get() = _class + and set(cls) = _class <- cls + + 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 \ No newline at end of file From ccefbffe4887726d0be30347c41e0e357579a470 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 17:52:56 +0200 Subject: [PATCH 23/49] add steps decoding --- src/CWL/Decode.fs | 66 ++++++++++++++++++++++++++++++++++++++++ src/CWL/Inputs.fs | 4 +-- src/CWL/Outputs.fs | 2 +- src/CWL/WorkflowSteps.fs | 10 ++++-- 4 files changed, 77 insertions(+), 5 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index a38c96bd..fb53bdf2 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -5,7 +5,10 @@ open YAMLicious.YAMLiciousTypes open CWLTypes open Requirements open Inputs +open Inputs.Workflow open Outputs +open Outputs.Workflow +open WorkflowSteps open DynamicObj module Decode = @@ -314,3 +317,66 @@ module Decode = description.BaseCommand <- baseCommand description + 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 -> StepInput []) = + Decode.object (fun get -> + let dict = get.Overflow.FieldList [] + [| + for key in dict.Keys do + let value = dict.[key] + let source = stringOptionFieldDecoder "source" value + let defaultValue = stringOptionFieldDecoder "default" value + let valueFrom = stringOptionFieldDecoder "valueFrom" value + { Id = key; Source = source; DefaultValue = defaultValue; ValueFrom = valueFrom } + |] + ) + + let outputStepsDecoder: (YAMLiciousTypes.YAMLElement -> string []) = + Decode.object (fun get -> + let outputs = get.Required.Field "out" (Decode.array 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 + |] + ) + + let stepsDecoder = + Decode.object (fun get -> + let steps = get.Required.Field "steps" stepArrayDecoder + steps + ) + diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index a7666685..1ab7c5c7 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -31,6 +31,6 @@ module Inputs = type StepInput = { Id: string Source: string option - defaultValue: string option - valueFrom: string option + DefaultValue: string option + ValueFrom: string option } diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index 44bca1e8..a26eed4a 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -25,5 +25,5 @@ module Outputs = module Workflow = type StepOutput = { - Id: string + Id: string [] } \ No newline at end of file diff --git a/src/CWL/WorkflowSteps.fs b/src/CWL/WorkflowSteps.fs index 6b8945d5..775d7f34 100644 --- a/src/CWL/WorkflowSteps.fs +++ b/src/CWL/WorkflowSteps.fs @@ -9,20 +9,26 @@ open DynamicObj module WorkflowSteps = type WorkflowStep ( + id: string, in_: StepInput [], - out_: StepOutput [], + out_: StepOutput, run: string, ?requirements: Requirement [], ?hints: Requirement [] ) = inherit DynamicObj () + let mutable _id: string = id let mutable _in: StepInput [] = in_ - let mutable _out: StepOutput [] = out_ + let mutable _out: StepOutput = out_ let mutable _run: string = run let mutable _requirements: Requirement [] option = requirements let mutable _hints: Requirement [] option = hints + member this.Id + with get() = _id + and set(id) = _id <- id + member this.In with get() = _in and set(in_) = _in <- in_ From 01a3c04161f392ae21a5659bf168f2b912532182 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 19:32:17 +0200 Subject: [PATCH 24/49] fix direct map decode --- src/CWL/Decode.fs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index fb53bdf2..26a7a108 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -335,7 +335,16 @@ module Decode = [| for key in dict.Keys do let value = dict.[key] - let source = stringOptionFieldDecoder "source" value + 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 } From 3db45c64eff8b521907495a992fee34ddde79463 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 19:37:02 +0200 Subject: [PATCH 25/49] add WorkflowSteps tests --- tests/CWL/ARCtrl.CWL.Tests.fsproj | 1 + tests/CWL/Main.fs | 1 + tests/CWL/WorkflowSteps.Tests.fs | 82 +++++++++++++++++++ .../TestObjects.CWL/WorkflowSteps.fs | 23 ++++++ tests/TestingUtils/TestingUtils.fsproj | 1 + 5 files changed, 108 insertions(+) create mode 100644 tests/CWL/WorkflowSteps.Tests.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index 5347a1c3..643c094c 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -10,6 +10,7 @@ + diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs index 2e356660..b5e22350 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -7,6 +7,7 @@ let all = testSequenced <| testList "CWL" [ Tests.Outputs.testOutput Tests.Inputs.testInput Tests.Requirements.testRequirement + Tests.WorkflowSteps.testWorkflowStep ] [] diff --git a/tests/CWL/WorkflowSteps.Tests.fs b/tests/CWL/WorkflowSteps.Tests.fs new file mode 100644 index 00000000..94bbc4b0 --- /dev/null +++ b/tests/CWL/WorkflowSteps.Tests.fs @@ -0,0 +1,82 @@ +module Tests.WorkflowSteps + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.WorkflowSteps +open ARCtrl.CWL.Outputs.Workflow +open ARCtrl.CWL.Inputs.Workflow +open YAMLicious +open TestingUtils + +let decodeWorkflowStep = + TestObjects.CWL.WorkflowSteps.workflowSteps + |> Decode.read + |> Decode.stepsDecoder + +let testWorkflowStep = + testList "Decode WorkflowStep" [ + testCase "Length" <| fun _ -> Expect.isTrue (2 = decodeWorkflowStep.Length) "Length of WorkflowSteps is not 2" + testList "IDs" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = "MzMLToMzlite" + let actual = decodeWorkflowStep.[0].Id + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = "PeptideSpectrumMatching" + let actual = decodeWorkflowStep.[1].Id + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "Run" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = "./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl" + let actual = decodeWorkflowStep.[0].Run + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = "./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl" + let actual = decodeWorkflowStep.[1].Run + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "In" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = [| + {Id = "stageDirectory"; Source = Some "stage"; DefaultValue = None; ValueFrom = None}; + {Id = "inputDirectory"; Source = Some "inputMzML"; DefaultValue = None; ValueFrom = None} + |] + let actual = decodeWorkflowStep.[0].In + Expect.isTrue + (expected = actual) + $"Expected:\n{expected.[0]}\n{expected.[1]}\nActual:\n{actual.[0]}\n{actual.[1]}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = [| + {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.isTrue + (expected = actual) + $"Expected:\n{expected.[0]}\n{expected.[1]}\n{expected.[2]}\n{expected.[3]}\nActual:\n{actual.[0]}\n{actual.[1]}\n{actual.[2]}\n{actual.[3]}" + ] + testList "Out" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = {Id = [|"dir"|]} + let actual = decodeWorkflowStep.[0].Out + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = {Id = [|"dir1";"dir2"|]} + let actual = decodeWorkflowStep.[1].Out + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] \ No newline at end of file diff --git a/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs b/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs new file mode 100644 index 00000000..32d2f492 --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs @@ -0,0 +1,23 @@ +module TestObjects.CWL.WorkflowSteps + +let workflowSteps ="""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 9145651e..0ebf6e5e 100644 --- a/tests/TestingUtils/TestingUtils.fsproj +++ b/tests/TestingUtils/TestingUtils.fsproj @@ -5,6 +5,7 @@ true + From 95dab475dc58269b1983af26d9e35e65fc6c12e9 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 20:30:31 +0200 Subject: [PATCH 26/49] support direct type mappings for inputs and outputs --- src/CWL/Decode.fs | 74 +++++++++++-------- tests/CWL/Outputs.Tests.fs | 29 +++++++- tests/TestingUtils/TestObjects.CWL/Inputs.fs | 3 +- tests/TestingUtils/TestObjects.CWL/Outputs.fs | 3 +- 4 files changed, 71 insertions(+), 38 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 26a7a108..d9d4ddd5 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -19,9 +19,9 @@ module Decode = { Glob = glob } ) - let outputBindingDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding) = + let outputBindingDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding option) = Decode.object(fun get -> - let outputBinding = get.Required.Field "outputBinding" outputBindingGlobDecoder + let outputBinding = get.Optional.Field "outputBinding" outputBindingGlobDecoder outputBinding ) @@ -41,6 +41,30 @@ module Decode = | _ -> failwith "Invalid CWL type" ) + let cwlTypeStringMatcher t = + match t with + | "File" -> File (FileInstance ()) + | "Directory" -> Directory (DirectoryInstance ()) + | "Dirent" -> Dirent { Entry = ""; Entryname = None; Writable = None } + | "string" -> String + | "int" -> Int + | "long" -> Long + | "float" -> Float + | "double" -> Double + | "boolean" -> Boolean + | "File[]" -> Array (File (FileInstance ())) + | "Directory[]" -> Array (Directory (DirectoryInstance ())) + | "Dirent[]" -> Array (Dirent { Entry = ""; Entryname = None; Writable = None }) + | "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" + let cwlTypeDecoder: (YAMLiciousTypes.YAMLElement -> CWLType) = Decode.object (fun get -> let cwlType = @@ -55,28 +79,7 @@ module Decode = ) match cwlType with | Some t -> - match t with - | "File" -> File (FileInstance ()) - | "Directory" -> Directory (DirectoryInstance ()) - | "Dirent" -> Dirent { Entry = ""; Entryname = None; Writable = None } - | "string" -> String - | "int" -> Int - | "long" -> Long - | "float" -> Float - | "double" -> Double - | "boolean" -> Boolean - | "File[]" -> Array (File (FileInstance ())) - | "Directory[]" -> Array (Directory (DirectoryInstance ())) - | "Dirent[]" -> Array (Dirent { Entry = ""; Entryname = None; Writable = None }) - | "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" + cwlTypeStringMatcher t | None -> let cwlTypeArray = get.Required.Field "type" cwlArrayTypeDecoder cwlTypeArray @@ -89,12 +92,18 @@ module Decode = for key in dict.Keys do let value = dict.[key] let outputBinding = outputBindingDecoder value - let cwlType = cwlTypeDecoder value - Output( - key, - cwlType, - outputBinding - ) + let cwlType = + match value with + | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value + | _ -> cwlTypeDecoder value + let output = + Output( + key, + cwlType + ) + if outputBinding.IsSome then + DynObj.setValueOpt output "outputBinding" outputBinding + output |] ) @@ -258,7 +267,10 @@ module Decode = for key in dict.Keys do let value = dict.[key] let inputBinding = inputBindingDecoder value - let cwlType = cwlTypeDecoder value + let cwlType = + match value with + | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value + | _ -> cwlTypeDecoder value let input = Input( key, diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs index b28c3d81..f4c331b3 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -14,7 +14,7 @@ let decodeOutput = let testOutput = testList "outputs with basetypes and array" [ testCase "Length" <| fun _ -> - let expected = 4 + let expected = 5 let actual = decodeOutput.Length Expect.isTrue (expected = actual) @@ -43,7 +43,7 @@ let testOutput = testList "Directory" [ let directoryItem = decodeOutput.[1] testCase "Name" <| fun _ -> - let expected = "example" + let expected = "example1" let actual = directoryItem.Name Expect.isTrue (expected = actual) @@ -61,8 +61,29 @@ let testOutput = (expected = actual) $"Expected: {expected}\nActual: {actual}" ] + testList "Directory 2" [ + let directoryItem = decodeOutput.[2] + testCase "Name" <| fun _ -> + let expected = "example2" + let actual = directoryItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Directory (DirectoryInstance()) + let actual = directoryItem.Type.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = None + let actual = directoryItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] testList "File Array" [ - let fileArrayItem = decodeOutput.[2] + let fileArrayItem = decodeOutput.[3] testCase "Name" <| fun _ -> let expected = "exampleArray1" let actual = fileArrayItem.Name @@ -83,7 +104,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" ] testList "File Array 2" [ - let fileArrayItem = decodeOutput.[3] + let fileArrayItem = decodeOutput.[4] testCase "Name" <| fun _ -> let expected = "exampleArray2" let actual = fileArrayItem.Name diff --git a/tests/TestingUtils/TestObjects.CWL/Inputs.fs b/tests/TestingUtils/TestObjects.CWL/Inputs.fs index 4e3bcb45..907617af 100644 --- a/tests/TestingUtils/TestObjects.CWL/Inputs.fs +++ b/tests/TestingUtils/TestObjects.CWL/Inputs.fs @@ -1,8 +1,7 @@ module TestObjects.CWL.Inputs let inputs ="""inputs: - arcDirectory: - type: Directory + arcDirectory: Directory firstArg: type: File inputBinding: diff --git a/tests/TestingUtils/TestObjects.CWL/Outputs.fs b/tests/TestingUtils/TestObjects.CWL/Outputs.fs index 3a0b239e..8f7f1ad3 100644 --- a/tests/TestingUtils/TestObjects.CWL/Outputs.fs +++ b/tests/TestingUtils/TestObjects.CWL/Outputs.fs @@ -5,10 +5,11 @@ let outputs ="""outputs: type: File outputBinding: glob: ./arc/runs/fsResult1/result.csv - example: + example1: type: Directory outputBinding: glob: ./arc/runs/fsResult1/example.csv + example2: Directory exampleArray1: type: File[] outputBinding: From f53e73f8adfcf39c820d3b67a91565594a36cf05 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 20:34:57 +0200 Subject: [PATCH 27/49] remove try with for dirent initial workdir --- src/CWL/Decode.fs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index d9d4ddd5..48595d6a 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -164,25 +164,23 @@ module Decode = let initialWorkDirRequirementDecoder (get: Decode.IGetters): CWLType[] = let initialWorkDir = - try - get.Required.Field - "listing" - ( - Decode.array - ( - Decode.object (fun get2 -> - Dirent - { - // BUG: Entry Requires an Entryname to be present when it's an expression - Entry = get2.Required.Field "entry" decodeStringOrExpression - Entryname = get2.Optional.Field "entryname" decodeStringOrExpression - Writable = get2.Optional.Field "writable" Decode.bool - } - ) + //TODO: Support more than dirent + get.Required.Field + "listing" + ( + Decode.array + ( + Decode.object (fun get2 -> + Dirent + { + // BUG: Entry Requires an Entryname to be present when it's an expression + Entry = get2.Required.Field "entry" decodeStringOrExpression + Entryname = get2.Optional.Field "entryname" decodeStringOrExpression + Writable = get2.Optional.Field "writable" Decode.bool + } ) + ) ) - with - | _ -> failwith "Only Dirent supported" initialWorkDir let resourceRequirementDecoder (get: Decode.IGetters): ResourceRequirementInstance = From 0fd5be97fc9e5733e7e01836462b0c2d2b1580e1 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Sun, 6 Oct 2024 21:57:52 +0200 Subject: [PATCH 28/49] add overflow decoder --- src/CWL/Decode.fs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 48595d6a..3ff9eb02 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -399,3 +399,19 @@ module Decode = steps ) + 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.setValue dynObj e.Key v.Value + | 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.setValue + dynObj + e.Key + x + ) + dynObj + From 7007a0b1e58f8438a47beabdcfcb288716abe3e6 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 8 Oct 2024 12:18:20 +0200 Subject: [PATCH 29/49] add cmd tool metadata and tests --- src/CWL/CWL.fs | 18 +- src/CWL/Decode.fs | 63 +++++-- tests/CWL/ARCtrl.CWL.Tests.fsproj | 1 + tests/CWL/CWLObject.Tests.fs | 6 + tests/CWL/CWLObjectMetadata.Tests.fs | 174 ++++++++++++++++++ tests/CWL/Main.fs | 1 + .../CommandLineToolMetadata.fs | 108 +++++++++++ tests/TestingUtils/TestingUtils.fsproj | 1 + 8 files changed, 353 insertions(+), 19 deletions(-) create mode 100644 tests/CWL/CWLObjectMetadata.Tests.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index 21b32e13..10205977 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -16,7 +16,8 @@ module CWLProcessingUnits = ?baseCommand: string [], ?requirements: Requirement [], ?hints: Requirement [], - ?inputs: Input [] + ?inputs: Input [], + ?metadata: DynamicObj ) = inherit DynamicObj () @@ -27,6 +28,7 @@ module CWLProcessingUnits = let mutable _requirements: Requirement [] option = requirements let mutable _hints: Requirement [] option = hints let mutable _inputs: Input [] option = inputs + let mutable _metadata: DynamicObj option = metadata member this.CWLVersion with get() = _cwlVersion @@ -56,6 +58,10 @@ module CWLProcessingUnits = with get() = _inputs and set(inputs) = _inputs <- inputs + member this.Metadata + with get() = _metadata + and set(metadata) = _metadata <- metadata + type CWLWorkflowDescription( cwlVersion: string, cls: Class, @@ -63,7 +69,8 @@ module CWLProcessingUnits = inputs: Input [], outputs: Output [], ?requirements: Requirement [], - ?hints: Requirement [] + ?hints: Requirement [], + ?metadata: DynamicObj ) = inherit DynamicObj() @@ -74,6 +81,7 @@ module CWLProcessingUnits = let mutable _outputs: Output [] = outputs let mutable _requirements: Requirement [] option = requirements let mutable _hints: Requirement [] option = hints + let mutable _metadata: DynamicObj option = metadata member this.CWLVersion with get() = _cwlVersion @@ -101,4 +109,8 @@ module CWLProcessingUnits = member this.Hints with get() = _hints - and set(hints) = _hints <- hints \ No newline at end of file + 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/Decode.fs b/src/CWL/Decode.fs index 3ff9eb02..77f01deb 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -12,6 +12,22 @@ open WorkflowSteps open DynamicObj module Decode = + + 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.setValue dynObj e.Key v.Value + | 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.setValue + dynObj + e.Key + x + ) + dynObj let outputBindingGlobDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding) = Decode.object (fun get -> @@ -317,6 +333,35 @@ module Decode = CommandLineTool, outputs ) + 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 if inputs.IsSome then description.Inputs <- inputs if requirements.IsSome then @@ -325,6 +370,8 @@ module Decode = description.Hints <- hints if baseCommand.IsSome then description.BaseCommand <- baseCommand + if metadata.Properties.Count > 0 then + description.Metadata <- Some metadata description let stringOptionFieldDecoder field : (YAMLiciousTypes.YAMLElement -> string option) = @@ -399,19 +446,3 @@ module Decode = steps ) - 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.setValue dynObj e.Key v.Value - | 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.setValue - dynObj - e.Key - x - ) - dynObj - diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index 643c094c..efd8ac36 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -7,6 +7,7 @@ + diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index f9331d34..623f2078 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -164,4 +164,10 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" ] ] + testCase "Metadata" <| fun _ -> + let expected = None + let actual = decodeCWLToolDescription.Metadata + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" ] \ No newline at end of file diff --git a/tests/CWL/CWLObjectMetadata.Tests.fs b/tests/CWL/CWLObjectMetadata.Tests.fs new file mode 100644 index 00000000..b6771bf5 --- /dev/null +++ b/tests/CWL/CWLObjectMetadata.Tests.fs @@ -0,0 +1,174 @@ +module Tests.CWLObjectMetadata + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs +open DynamicObj +open YAMLicious +open TestingUtils + +let decodeCWLToolDescription = + TestObjects.CWL.CommandLineToolMetadata.cwl + |> Decode.decodeCommandLineTool + +let testCWLToolDescriptionMetadata = + testList "CWLToolDescription" [ + testCase "Class" <| fun _ -> + let expected = Class.CommandLineTool + let actual = decodeCWLToolDescription.Class + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "CWLVersion" <| fun _ -> + let expected = "v1.2" + let actual = decodeCWLToolDescription.CWLVersion + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "baseCommand" <| fun _ -> + let expected = Some [|"dotnet"; "fsi"; "script.fsx"|] + let actual = decodeCWLToolDescription.BaseCommand + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + 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.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "Requirements" [ + let requirementsItem = decodeCWLToolDescription.Requirements + testCase "InitialWorkDirRequirement" <| fun _ -> + let expected = InitialWorkDirRequirement [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|] + let actual = requirementsItem.Value.[0] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "EnvVarRequirement" <| fun _ -> + let expected = EnvVarRequirement [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|] + let actual = requirementsItem.Value.[1] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "NetworkAccessRequirement" <| fun _ -> + let expected = NetworkAccessRequirement + let actual = requirementsItem.Value.[2] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "Inputs" [ + let inputsItem = decodeCWLToolDescription.Inputs.Value + testCase "Length" <| fun _ -> + let expected = 2 + let actual = inputsItem.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testList "File" [ + let fileItem = inputsItem.[0] + testCase "Name" <| fun _ -> + let expected = "firstArg" + let actual = fileItem.Name + Expect.isTrue + ("firstArg" = fileItem.Name) + "Name of input is not 'firstArg'" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} + let actual = fileItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "String" [ + let stringItem = inputsItem.[1] + testCase "Name" <| fun _ -> + let expected = "secondArg" + let actual = stringItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = String + let actual = stringItem.Type.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} + let actual = stringItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] + testList "Outputs" [ + let outputsItem = decodeCWLToolDescription.Outputs + testCase "Length" <| fun _ -> + let expected = 2 + let actual = outputsItem.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testList "Directory" [ + let directoryItem = outputsItem.[0] + testCase "Name" <| fun _ -> + let expected = "output" + let actual = directoryItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Directory (DirectoryInstance()) + let actual = directoryItem.Type.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"} + let actual = directoryItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "File" [ + let fileItem = outputsItem.[1] + testCase "Name" <| fun _ -> + let expected = "output2" + let actual = fileItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"} + let actual = fileItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] + testCase "Metadata" <| fun _ -> + let expected = TestObjects.CWL.CommandLineToolMetadata.expectedMetadataString + let actual = decodeCWLToolDescription.Metadata.Value |> DynObj.format + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] \ No newline at end of file diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs index b5e22350..e31f32bc 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -4,6 +4,7 @@ open Fable.Pyxpecto let all = testSequenced <| testList "CWL" [ Tests.CWLObject.testCWLToolDescription + Tests.CWLObjectMetadata.testCWLToolDescriptionMetadata Tests.Outputs.testOutput Tests.Inputs.testInput Tests.Requirements.testRequirement diff --git a/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs b/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs new file mode 100644 index 00000000..ce1e581c --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs @@ -0,0 +1,108 @@ +module TestObjects.CWL.CommandLineToolMetadata + +let cwl ="""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/TestingUtils.fsproj b/tests/TestingUtils/TestingUtils.fsproj index 0ebf6e5e..07d01d8e 100644 --- a/tests/TestingUtils/TestingUtils.fsproj +++ b/tests/TestingUtils/TestingUtils.fsproj @@ -7,6 +7,7 @@ + From 17fabe8af66078f9f0834acb631c7a332a9ef4f0 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 8 Oct 2024 16:00:40 +0200 Subject: [PATCH 30/49] fix for new dynamic obj version --- src/CWL/CWLTypes.fs | 2 +- src/CWL/Decode.fs | 11 ++++++----- src/CWL/Inputs.fs | 8 ++++---- src/CWL/Outputs.fs | 8 ++++---- src/CWL/Requirements.fs | 16 ++++++++-------- 5 files changed, 23 insertions(+), 22 deletions(-) diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs index b3164fc4..7bb1cf40 100644 --- a/src/CWL/CWLTypes.fs +++ b/src/CWL/CWLTypes.fs @@ -48,7 +48,7 @@ module CWLTypes = type SchemaDefRequirementType (types, definitions) as this = inherit DynamicObj () do - DynObj.setValue this (nameof types) definitions + DynObj.setProperty (nameof types) definitions this type SoftwarePackage = { Package: string diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 77f01deb..558cd5c7 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -17,16 +17,17 @@ module Decode = for e in dict do match e.Value with | YAMLElement.Object [YAMLElement.Value v] -> - DynObj.setValue dynObj e.Key v.Value + 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.setValue - dynObj + DynObj.setProperty e.Key x + dynObj ) + | _ -> DynObj.setProperty e.Key e.Value dynObj dynObj let outputBindingGlobDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding) = @@ -118,7 +119,7 @@ module Decode = cwlType ) if outputBinding.IsSome then - DynObj.setValueOpt output "outputBinding" outputBinding + DynObj.setOptionalProperty "outputBinding" outputBinding output output |] ) @@ -291,7 +292,7 @@ module Decode = cwlType ) if inputBinding.IsSome then - DynObj.setValueOpt input "inputBinding" inputBinding + DynObj.setOptionalProperty "inputBinding" inputBinding input input |] ) diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 1ab7c5c7..4493dce7 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -20,11 +20,11 @@ module Inputs = ) as this = inherit DynamicObj () do - DynObj.setValueOpt this ("type") type_ - DynObj.setValueOpt this ("inputBinding") inputBinding + DynObj.setOptionalProperty ("type") type_ this + DynObj.setOptionalProperty ("inputBinding") inputBinding this member this.Name = name - member this.Type = DynObj.tryGetTypedValue ("type") this - member this.InputBinding = DynObj.tryGetTypedValue ("inputBinding") this + member this.Type = DynObj.tryGetTypedPropertyValue ("type") this + member this.InputBinding = DynObj.tryGetTypedPropertyValue ("inputBinding") this module Workflow = diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index a26eed4a..50991512 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -16,11 +16,11 @@ module Outputs = ) as this = inherit DynamicObj () do - DynObj.setValueOpt this ("type") type_ - DynObj.setValueOpt this ("outputBinding") outputBinding + DynObj.setOptionalProperty ("type") type_ this + DynObj.setOptionalProperty ("outputBinding") outputBinding this member this.Name = name - member this.Type = DynObj.tryGetTypedValue ("type") this - member this.OutputBinding = DynObj.tryGetTypedValue ("outputBinding") this + member this.Type = DynObj.tryGetTypedPropertyValue ("type") this + member this.OutputBinding = DynObj.tryGetTypedPropertyValue ("outputBinding") this module Workflow = diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index 93ba0c38..00a6503a 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -29,14 +29,14 @@ module Requirements = ) as this = inherit DynamicObj () do - DynObj.setValueOpt this (nameof coresMin) coresMin - DynObj.setValueOpt this (nameof coresMax) coresMax - DynObj.setValueOpt this (nameof ramMin) ramMin - DynObj.setValueOpt this (nameof ramMax) ramMax - DynObj.setValueOpt this (nameof tmpdirMin) tmpdirMin - DynObj.setValueOpt this (nameof tmpdirMax) tmpdirMax - DynObj.setValueOpt this (nameof outdirMin) outdirMin - DynObj.setValueOpt this (nameof outdirMax) outdirMax + 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 = | InlineJavascriptRequirement From 3fca0c11671bd3cd3f75f5473bb43c1baf793a0b Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 8 Oct 2024 16:01:11 +0200 Subject: [PATCH 31/49] update fsproj --- src/CWL/ARCtrl.CWL.fsproj | 14 ++------------ src/Package.Metadata.props | 2 +- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/src/CWL/ARCtrl.CWL.fsproj b/src/CWL/ARCtrl.CWL.fsproj index 655ca106..97dc3a7a 100644 --- a/src/CWL/ARCtrl.CWL.fsproj +++ b/src/CWL/ARCtrl.CWL.fsproj @@ -21,17 +21,7 @@ - - + + - - nfdi4plants - ARC helper functions for Common workflow language. - MIT - logo.png - ARC F# FSharp dotnet .Net bioinformatics biology fable-library datascience dataplant nfdi metadata - https://github.com/nfdi4plants/ARCtrl/tree/main/src/CWL - https://github.com/nfdi4plants/ARCtrl - git - 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 From 4ff39f9f55b85a89f123de5db4aedb5dc3433c7d Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 8 Oct 2024 16:12:28 +0200 Subject: [PATCH 32/49] Update YAMLicious to version 0.0.2 --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 97925793..bc574348 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,7 +11,7 @@ - + From ff5af9c51afc9f2268f3bbab054737ededd9fb87 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 8 Oct 2024 16:55:41 +0200 Subject: [PATCH 33/49] rename Type to Type_ for fable --- src/CWL/Inputs.fs | 2 +- src/CWL/Outputs.fs | 2 +- tests/CWL/CWLObject.Tests.fs | 8 ++++---- tests/CWL/CWLObjectMetadata.Tests.fs | 8 ++++---- tests/CWL/Inputs.Tests.fs | 6 +++--- tests/CWL/Outputs.Tests.fs | 10 +++++----- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 4493dce7..360dbcef 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -23,7 +23,7 @@ module Inputs = DynObj.setOptionalProperty ("type") type_ this DynObj.setOptionalProperty ("inputBinding") inputBinding this member this.Name = name - member this.Type = DynObj.tryGetTypedPropertyValue ("type") this + member this.Type_ = DynObj.tryGetTypedPropertyValue ("type") this member this.InputBinding = DynObj.tryGetTypedPropertyValue ("inputBinding") this module Workflow = diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index 50991512..0b63b4fc 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -19,7 +19,7 @@ module Outputs = DynObj.setOptionalProperty ("type") type_ this DynObj.setOptionalProperty ("outputBinding") outputBinding this member this.Name = name - member this.Type = DynObj.tryGetTypedPropertyValue ("type") this + member this.Type_ = DynObj.tryGetTypedPropertyValue ("type") this member this.OutputBinding = DynObj.tryGetTypedPropertyValue ("outputBinding") this module Workflow = diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 623f2078..197e24d4 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -80,7 +80,7 @@ let testCWLToolDescription = "Name of input is not 'firstArg'" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type.Value + let actual = fileItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -101,7 +101,7 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = String - let actual = stringItem.Type.Value + let actual = stringItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -131,7 +131,7 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type.Value + let actual = directoryItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -152,7 +152,7 @@ let testCWLToolDescription = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type.Value + let actual = fileItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" diff --git a/tests/CWL/CWLObjectMetadata.Tests.fs b/tests/CWL/CWLObjectMetadata.Tests.fs index b6771bf5..f93805dd 100644 --- a/tests/CWL/CWLObjectMetadata.Tests.fs +++ b/tests/CWL/CWLObjectMetadata.Tests.fs @@ -81,7 +81,7 @@ let testCWLToolDescriptionMetadata = "Name of input is not 'firstArg'" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type.Value + let actual = fileItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -102,7 +102,7 @@ let testCWLToolDescriptionMetadata = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = String - let actual = stringItem.Type.Value + let actual = stringItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -132,7 +132,7 @@ let testCWLToolDescriptionMetadata = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type.Value + let actual = directoryItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -153,7 +153,7 @@ let testCWLToolDescriptionMetadata = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type.Value + let actual = fileItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs index 8a7aabb1..92afcc37 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -18,12 +18,12 @@ let testInput = testList "Directory" [ let directoryItem = decodeInput.[0] testCase "Name" <| fun _ -> Expect.isTrue ("arcDirectory" = directoryItem.Name) "Name of input is not 'arcDirectory'" - testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type.Value) "Type of input is not Directory" + testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type_.Value) "Type of input is not Directory" ] testList "File" [ let fileItem = decodeInput.[1] testCase "Name" <| fun _ -> Expect.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" - testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type.Value) "Type of input is not File" + testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type_.Value) "Type of input is not File" testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = Some "--example"; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" ] testList "String" [ @@ -31,7 +31,7 @@ let testInput = testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" testCase "Type" <| fun _ -> let expected = String - let actual = stringItem.Type.Value + let actual = stringItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs index f4c331b3..be3d161a 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -29,7 +29,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = File (FileInstance()) - let actual = fileItem.Type.Value + let actual = fileItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -50,7 +50,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type.Value + let actual = directoryItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -71,7 +71,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type.Value + let actual = directoryItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -92,7 +92,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Array (File (FileInstance())) - let actual = fileArrayItem.Type.Value + let actual = fileArrayItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" @@ -113,7 +113,7 @@ let testOutput = $"Expected: {expected}\nActual: {actual}" testCase "Type" <| fun _ -> let expected = Array (File (FileInstance())) - let actual = fileArrayItem.Type.Value + let actual = fileArrayItem.Type_.Value Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" From 379efb4d4e0c0fb023947557c927dbb785dba609 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 8 Oct 2024 18:04:38 +0200 Subject: [PATCH 34/49] add failcase to requirements --- src/CWL/Decode.fs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 558cd5c7..47ba5541 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -242,6 +242,7 @@ module Decode = | "ShellCommandRequirement" -> ShellCommandRequirement | "ResourceRequirement" -> ResourceRequirement (resourceRequirementDecoder get) | "NetworkAccess" -> NetworkAccessRequirement + | _ -> failwith "Invalid requirement" ) ) From 44112a8638930494eabd379418b255df982fcd9a Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 8 Oct 2024 18:06:51 +0200 Subject: [PATCH 35/49] add wf requirements to decoder --- src/CWL/Decode.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 47ba5541..699f0d48 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -242,6 +242,10 @@ module Decode = | "ShellCommandRequirement" -> ShellCommandRequirement | "ResourceRequirement" -> ResourceRequirement (resourceRequirementDecoder get) | "NetworkAccess" -> NetworkAccessRequirement + | "SubworkflowFeatureRequirement" -> SubworkflowFeatureRequirement + | "ScatterFeatureRequirement" -> ScatterFeatureRequirement + | "MultipleInputFeatureRequirement" -> MultipleInputFeatureRequirement + | "StepInputExpressionRequirement" -> StepInputExpressionRequirement | _ -> failwith "Invalid requirement" ) ) From 548b2b754913ab043763ecfd82bd76289f7f5856 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 11 Oct 2024 15:54:39 +0200 Subject: [PATCH 36/49] add metadata and dynobj testing (fix test runs) --- src/CWL/Decode.fs | 2 +- tests/CWL/ARCtrl.CWL.Tests.fsproj | 1 + tests/CWL/CWLObjectMetadata.Tests.fs | 1 + tests/CWL/Main.fs | 1 + tests/CWL/Metadata.Tests.fs | 41 +++++++++++++++++++ .../TestingUtils/TestObjects.CWL/Metadata.fs | 38 +++++++++++++++++ tests/TestingUtils/TestingUtils.fsproj | 1 + 7 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 tests/CWL/Metadata.Tests.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/Metadata.fs diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 699f0d48..49dbcd40 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -376,7 +376,7 @@ module Decode = description.Hints <- hints if baseCommand.IsSome then description.BaseCommand <- baseCommand - if metadata.Properties.Count > 0 then + if metadata.GetProperties(false) |> Seq.length > 0 then description.Metadata <- Some metadata description diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index efd8ac36..a8876405 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -8,6 +8,7 @@ + diff --git a/tests/CWL/CWLObjectMetadata.Tests.fs b/tests/CWL/CWLObjectMetadata.Tests.fs index f93805dd..c20081af 100644 --- a/tests/CWL/CWLObjectMetadata.Tests.fs +++ b/tests/CWL/CWLObjectMetadata.Tests.fs @@ -166,6 +166,7 @@ let testCWLToolDescriptionMetadata = ] ] testCase "Metadata" <| fun _ -> + Expect.isSome decodeCWLToolDescription.Metadata $"Expected {decodeCWLToolDescription.Metadata} to be Some" let expected = TestObjects.CWL.CommandLineToolMetadata.expectedMetadataString let actual = decodeCWLToolDescription.Metadata.Value |> DynObj.format Expect.isTrue diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs index e31f32bc..21eeab11 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -5,6 +5,7 @@ open Fable.Pyxpecto let all = testSequenced <| testList "CWL" [ Tests.CWLObject.testCWLToolDescription Tests.CWLObjectMetadata.testCWLToolDescriptionMetadata + Tests.Metadata.testMetadata Tests.Outputs.testOutput Tests.Inputs.testInput Tests.Requirements.testRequirement diff --git a/tests/CWL/Metadata.Tests.fs b/tests/CWL/Metadata.Tests.fs new file mode 100644 index 00000000..602376bd --- /dev/null +++ b/tests/CWL/Metadata.Tests.fs @@ -0,0 +1,41 @@ +module Tests.Metadata + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs +open ARCtrl.CWL.Decode +open DynamicObj +open YAMLicious +open TestingUtils + +let decodeMetadata = + TestObjects.CWL.Metadata.metadata + |> Decode.read + +let overflowDictionary = + decodeMetadata + |> Decode.object (fun get -> get.Overflow.FieldList []) + +let dynObj = + overflowDecoder (new DynamicObj()) overflowDictionary + +let testMetadata = + testList "CWL Metadata" [ + 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 + $"Expected: {expected}\nActual: {actual}" + 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 + $"Expected: {expected}\nActual: {actual}" + testCase "DynObj setProperty Value check" <| fun _ -> + let expectedValue = ".NET" + let actualValue = dynObj |> DynObj.tryGetTypedPropertyValue "arc:technology platform" + Expect.equal actualValue.Value expectedValue + $"Expected: {expectedValue}\nActual: {actualValue}" + ] \ 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..733084f2 --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/Metadata.fs @@ -0,0 +1,38 @@ +module TestObjects.CWL.Metadata + +let metadata ="""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/TestingUtils.fsproj b/tests/TestingUtils/TestingUtils.fsproj index 07d01d8e..245df511 100644 --- a/tests/TestingUtils/TestingUtils.fsproj +++ b/tests/TestingUtils/TestingUtils.fsproj @@ -8,6 +8,7 @@ + From 8b6963edfa4a83f891d3e029c709054ce7aa4d33 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 11 Oct 2024 16:52:29 +0200 Subject: [PATCH 37/49] trim strings for python --- tests/CWL/CWLObjectMetadata.Tests.fs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/CWL/CWLObjectMetadata.Tests.fs b/tests/CWL/CWLObjectMetadata.Tests.fs index c20081af..a913bc06 100644 --- a/tests/CWL/CWLObjectMetadata.Tests.fs +++ b/tests/CWL/CWLObjectMetadata.Tests.fs @@ -167,9 +167,8 @@ let testCWLToolDescriptionMetadata = ] testCase "Metadata" <| fun _ -> Expect.isSome decodeCWLToolDescription.Metadata $"Expected {decodeCWLToolDescription.Metadata} to be Some" - let expected = TestObjects.CWL.CommandLineToolMetadata.expectedMetadataString - let actual = decodeCWLToolDescription.Metadata.Value |> DynObj.format - Expect.isTrue - (expected = actual) + let expected = TestObjects.CWL.CommandLineToolMetadata.expectedMetadataString.Trim().Replace("\r\n", "\n") + let actual = (decodeCWLToolDescription.Metadata.Value |> DynObj.format).Trim().Replace("\r\n", "\n") + Expect.equal actual expected $"Expected: {expected}\nActual: {actual}" ] \ No newline at end of file From 7728ebbd168ff4f4b0301b81460149f89655e973 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 15 Oct 2024 13:43:18 +0200 Subject: [PATCH 38/49] update yamlicious version --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index bc574348..11ea1005 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -11,7 +11,7 @@ - + From 1222f018133f51baebd6d5faafc7ca12067056d5 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 15 Oct 2024 17:53:46 +0200 Subject: [PATCH 39/49] add workflow decoding --- src/CWL/Decode.fs | 250 ++++++++++++------ src/CWL/Outputs.fs | 4 +- tests/CWL/ARCtrl.CWL.Tests.fsproj | 1 + tests/CWL/CWLWorkflow.Tests.fs | 133 ++++++++++ tests/CWL/Main.fs | 1 + .../TestingUtils/TestObjects.CWL/Workflow.fs | 47 ++++ tests/TestingUtils/TestingUtils.fsproj | 1 + 7 files changed, 352 insertions(+), 85 deletions(-) create mode 100644 tests/CWL/CWLWorkflow.Tests.fs create mode 100644 tests/TestingUtils/TestObjects.CWL/Workflow.fs diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 49dbcd40..daf30297 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -29,7 +29,13 @@ module Decode = ) | _ -> DynObj.setProperty e.Key e.Value dynObj dynObj - + + 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 + let outputBindingGlobDecoder: (YAMLiciousTypes.YAMLElement -> OutputBinding) = Decode.object (fun get -> let glob = get.Optional.Field "glob" Decode.string @@ -42,13 +48,24 @@ module Decode = outputBinding ) + 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 + } + ) + 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 (Dirent { Entry = ""; Entryname = None; Writable = None }) + | "Dirent" -> Array (get.Required.Field "listing" direntDecoder) | "string" -> Array String | "int" -> Array Int | "long" -> Array Long @@ -58,11 +75,11 @@ module Decode = | _ -> failwith "Invalid CWL type" ) - let cwlTypeStringMatcher t = + let cwlTypeStringMatcher t (get: Decode.IGetters) = match t with | "File" -> File (FileInstance ()) | "Directory" -> Directory (DirectoryInstance ()) - | "Dirent" -> Dirent { Entry = ""; Entryname = None; Writable = None } + | "Dirent" -> (get.Required.Field "listing" direntDecoder) | "string" -> String | "int" -> Int | "long" -> Long @@ -71,7 +88,7 @@ module Decode = | "boolean" -> Boolean | "File[]" -> Array (File (FileInstance ())) | "Directory[]" -> Array (Directory (DirectoryInstance ())) - | "Dirent[]" -> Array (Dirent { Entry = ""; Entryname = None; Writable = None }) + | "Dirent[]" -> Array (get.Required.Field "listing" direntDecoder) | "string[]" -> Array String | "int[]" -> Array Int | "long[]" -> Array Long @@ -96,7 +113,7 @@ module Decode = ) match cwlType with | Some t -> - cwlTypeStringMatcher t + cwlTypeStringMatcher t get | None -> let cwlTypeArray = get.Required.Field "type" cwlArrayTypeDecoder cwlTypeArray @@ -109,9 +126,10 @@ module Decode = 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 + | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get | _ -> cwlTypeDecoder value let output = Output( @@ -120,6 +138,8 @@ module Decode = ) if outputBinding.IsSome then DynObj.setOptionalProperty "outputBinding" outputBinding output + if outputSource.IsSome then + DynObj.setOptionalProperty "outputSource" outputSource output output |] ) @@ -173,31 +193,12 @@ module Decode = ) envDef - 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 - let initialWorkDirRequirementDecoder (get: Decode.IGetters): CWLType[] = let initialWorkDir = //TODO: Support more than dirent get.Required.Field "listing" - ( - Decode.array - ( - Decode.object (fun get2 -> - Dirent - { - // BUG: Entry Requires an Entryname to be present when it's an expression - Entry = get2.Required.Field "entry" decodeStringOrExpression - Entryname = get2.Optional.Field "entryname" decodeStringOrExpression - Writable = get2.Optional.Field "writable" Decode.bool - } - ) - ) - ) + (Decode.array direntDecoder) initialWorkDir let resourceRequirementDecoder (get: Decode.IGetters): ResourceRequirementInstance = @@ -289,7 +290,7 @@ module Decode = let inputBinding = inputBindingDecoder value let cwlType = match value with - | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value + | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get | _ -> cwlTypeDecoder value let input = Input( @@ -324,62 +325,6 @@ module Decode = | "ExpressionTool" -> ExpressionTool | _ -> failwith "Invalid class" ) - - 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 = - CWLProcessingUnits.CWLToolDescription( - cwlVersion, - CommandLineTool, - outputs - ) - 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 - 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 - let stringOptionFieldDecoder field : (YAMLiciousTypes.YAMLElement -> string option) = Decode.object(fun get -> let fieldValue = get.Optional.Field field Decode.string @@ -452,3 +397,140 @@ module Decode = steps ) + 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 = + CWLProcessingUnits.CWLToolDescription( + cwlVersion, + CommandLineTool, + outputs + ) + 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 + + 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 + printfn "%A" steps + printfn "%A" outputs + printfn "%A" inputs + let description = + CWLProcessingUnits.CWLWorkflowDescription( + cwlVersion, + Workflow, + steps, + inputs, + outputs + ) + 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/Outputs.fs b/src/CWL/Outputs.fs index 0b63b4fc..034b488f 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -12,7 +12,8 @@ module Outputs = type Output ( name: string, ?type_: CWLType, - ?outputBinding: OutputBinding + ?outputBinding: OutputBinding, + ?outputSource: string ) as this = inherit DynamicObj () do @@ -21,6 +22,7 @@ module Outputs = 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 module Workflow = diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index a8876405..586424c8 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -6,6 +6,7 @@ false + diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs new file mode 100644 index 00000000..a603181b --- /dev/null +++ b/tests/CWL/CWLWorkflow.Tests.fs @@ -0,0 +1,133 @@ +module Tests.CWLWorkflow + +open ARCtrl.CWL +open ARCtrl.CWL.CWLTypes +open ARCtrl.CWL.Requirements +open ARCtrl.CWL.Inputs +open ARCtrl.CWL.Outputs +open ARCtrl.CWL.Outputs.Workflow +open ARCtrl.CWL.Inputs.Workflow +open YAMLicious +open TestingUtils + +let decodeCWLWorkflowDescription = + TestObjects.CWL.Workflow.workflow + |> Decode.decodeWorkflow + +let testCWLWorkflowDescription = + testList "CWLWorkflowDescription" [ + testCase "Class" <| fun _ -> + let expected = Class.Workflow + let actual = decodeCWLWorkflowDescription.Class + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + testCase "CWLVersion" <| fun _ -> + let expected = "v1.2" + let actual = decodeCWLWorkflowDescription.CWLVersion + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + testCase "MultipleInputFeatureRequirement" <| fun _ -> + let requirementsItem = decodeCWLWorkflowDescription.Requirements + let expected = MultipleInputFeatureRequirement + let actual = requirementsItem.Value.[0] + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + testCase "inputs" <| fun _ -> + let expected = [| + Input("cores", CWLType.Int); + Input("db", CWLType.File (FileInstance())); + Input ("stage", CWLType.Directory (DirectoryInstance())); + Input ("outputMzML", CWLType.Directory (DirectoryInstance())); + Input ("outputPSM", CWLType.Directory (DirectoryInstance())); + Input ("inputMzML", CWLType.Directory (DirectoryInstance())); + Input ("paramsMzML", CWLType.File (FileInstance())); + Input ("paramsPSM", CWLType.File (FileInstance())) + |] + let actual = decodeCWLWorkflowDescription.Inputs + Array.iter2 (fun (a: Input) (e: Input) -> + Expect.equal a.Name e.Name + $"Expected: {e}\nActual: {a}" + Expect.equal a.InputBinding e.InputBinding + $"Expected: {e}\nActual: {a}" + Expect.equal a.Type_ e.Type_ + $"Expected: {e}\nActual: {a}" + ) actual expected + testList "steps" [ + let workflowSteps = decodeCWLWorkflowDescription.Steps + testList "IDs" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = "MzMLToMzlite" + let actual = workflowSteps.[0].Id + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = "PeptideSpectrumMatching" + let actual = workflowSteps.[1].Id + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + ] + testList "Run" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = "./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl" + let actual = workflowSteps.[0].Run + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = "./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl" + let actual = workflowSteps.[1].Run + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + ] + testList "In" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = [| + {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.equal actual expected + $"Expected:\n{expected}\nActual:\n{actual}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = [| + {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.equal actual expected + $"Expected:\n{expected}\nActual:\n{actual}" + ] + testList "Out" [ + testCase "MzMLToMzlite" <| fun _ -> + let expected = {Id = [|"dir"|]} + let actual = workflowSteps.[0].Out + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + testCase "PeptideSpectrumMatching" <| fun _ -> + let expected = {Id = [|"dir"|]} + let actual = workflowSteps.[1].Out + Expect.equal actual expected + $"Expected: {expected}\nActual: {actual}" + ] + ] + testCase "outputs" <| fun _ -> + let expected = [| + Output("mzlite", CWLType.Directory (DirectoryInstance()), outputSource = "MzMLToMzlite/dir"); + Output("psm", CWLType.Directory (DirectoryInstance()), outputSource = "PeptideSpectrumMatching/dir") + |] + let actual = decodeCWLWorkflowDescription.Outputs + Array.iter2 (fun (a: Output) (e: Output) -> + Expect.equal a.Name e.Name + $"Expected: {e}\nActual: {a}" + Expect.equal a.Type_ e.Type_ + $"Expected: {e}\nActual: {a}" + Expect.equal a.OutputBinding e.OutputBinding + $"Expected: {e}\nActual: {a}" + ) actual expected + ] \ No newline at end of file diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs index 21eeab11..9654c783 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -3,6 +3,7 @@ module CWL.Tests open Fable.Pyxpecto let all = testSequenced <| testList "CWL" [ + Tests.CWLWorkflow.testCWLWorkflowDescription Tests.CWLObject.testCWLToolDescription Tests.CWLObjectMetadata.testCWLToolDescriptionMetadata Tests.Metadata.testMetadata diff --git a/tests/TestingUtils/TestObjects.CWL/Workflow.fs b/tests/TestingUtils/TestObjects.CWL/Workflow.fs new file mode 100644 index 00000000..4845c65a --- /dev/null +++ b/tests/TestingUtils/TestObjects.CWL/Workflow.fs @@ -0,0 +1,47 @@ +module TestObjects.CWL.Workflow + +let workflow ="""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/TestingUtils.fsproj b/tests/TestingUtils/TestingUtils.fsproj index 245df511..70dbba96 100644 --- a/tests/TestingUtils/TestingUtils.fsproj +++ b/tests/TestingUtils/TestingUtils.fsproj @@ -6,6 +6,7 @@ + From 83cad3e0579d01ffa5e5d04b938efa65ef300908 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Tue, 15 Oct 2024 18:45:47 +0200 Subject: [PATCH 40/49] add cwl to all test list --- tests/All/All.Tests.fsproj | 1 + tests/All/Main.fs | 2 +- tests/CWL/Main.fs | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) 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/Main.fs b/tests/CWL/Main.fs index 9654c783..7083fc19 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -1,4 +1,4 @@ -module CWL.Tests +module ARCtrl.CWL.Tests open Fable.Pyxpecto @@ -13,5 +13,7 @@ let all = testSequenced <| testList "CWL" [ Tests.WorkflowSteps.testWorkflowStep ] +#if !TESTS_ALL [] +#endif let main argv = Pyxpecto.runTests [||] all From f452622bde4bf2b10816a3d883869a8b6d3f3cfb Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Thu, 17 Oct 2024 12:47:31 +0200 Subject: [PATCH 41/49] rename types and tests --- src/ARCtrl/ARC.fs | 4 +- src/CWL/ARCtrl.CWL.fsproj | 3 - src/CWL/CWL.fs | 11 +- src/CWL/CWLTypes.fs | 2 +- src/CWL/Decode.fs | 3 +- tests/CWL/ARCtrl.CWL.Tests.fsproj | 1 - tests/CWL/CWLObject.Tests.fs | 179 +++++++++++++++++- tests/CWL/CWLObjectMetadata.Tests.fs | 174 ----------------- tests/CWL/CWLWorkflow.Tests.fs | 12 +- tests/CWL/Inputs.Tests.fs | 9 +- tests/CWL/Main.fs | 15 +- tests/CWL/Metadata.Tests.fs | 15 +- tests/CWL/Outputs.Tests.fs | 9 +- tests/CWL/Requirements.Tests.fs | 9 +- tests/CWL/WorkflowSteps.Tests.fs | 9 +- .../TestObjects.CWL/CommandLineTool.fs | 2 +- .../CommandLineToolMetadata.fs | 2 +- tests/TestingUtils/TestObjects.CWL/Inputs.fs | 2 +- .../TestingUtils/TestObjects.CWL/Metadata.fs | 2 +- tests/TestingUtils/TestObjects.CWL/Outputs.fs | 2 +- .../TestObjects.CWL/Requirements.fs | 2 +- .../TestingUtils/TestObjects.CWL/Workflow.fs | 2 +- .../TestObjects.CWL/WorkflowSteps.fs | 2 +- 23 files changed, 245 insertions(+), 226 deletions(-) delete mode 100644 tests/CWL/CWLObjectMetadata.Tests.fs diff --git a/src/ARCtrl/ARC.fs b/src/ARCtrl/ARC.fs index 53b4826a..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.CWLProcessingUnits.CWLToolDescription 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.CWLProcessingUnits.CWLToolDescription, ?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 97dc3a7a..1a98eda5 100644 --- a/src/CWL/ARCtrl.CWL.fsproj +++ b/src/CWL/ARCtrl.CWL.fsproj @@ -17,9 +17,6 @@ - - - diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index 10205977..9d1b2a5c 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -1,5 +1,6 @@ -namespace ARCtrl.CWL +namespace ARCtrl +open ARCtrl.CWL open DynamicObj open CWLTypes open Requirements @@ -11,7 +12,7 @@ module CWLProcessingUnits = type CWLToolDescription ( cwlVersion: string, - cls: Class, + cls: CWLClass, outputs: Output [], ?baseCommand: string [], ?requirements: Requirement [], @@ -22,7 +23,7 @@ module CWLProcessingUnits = inherit DynamicObj () let mutable _cwlVersion: string = cwlVersion - let mutable _class: Class = cls + let mutable _class: CWLClass = cls let mutable _outputs: Output [] = outputs let mutable _baseCommand: string [] option = baseCommand let mutable _requirements: Requirement [] option = requirements @@ -64,7 +65,7 @@ module CWLProcessingUnits = type CWLWorkflowDescription( cwlVersion: string, - cls: Class, + cls: CWLClass, steps: WorkflowStep [], inputs: Input [], outputs: Output [], @@ -75,7 +76,7 @@ module CWLProcessingUnits = inherit DynamicObj() let mutable _cwlVersion: string = cwlVersion - let mutable _class: Class = cls + let mutable _class: CWLClass = cls let mutable _steps: WorkflowStep [] = steps let mutable _inputs: Input [] = inputs let mutable _outputs: Output [] = outputs diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs index 7bb1cf40..7a40590c 100644 --- a/src/CWL/CWLTypes.fs +++ b/src/CWL/CWLTypes.fs @@ -31,7 +31,7 @@ module CWLTypes = | Null | Array of CWLType - type Class = + type CWLClass = | Workflow | CommandLineTool | ExpressionTool diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index daf30297..2e3dbd3c 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -1,5 +1,6 @@ namespace ARCtrl.CWL +open ARCtrl open YAMLicious open YAMLicious.YAMLiciousTypes open CWLTypes @@ -317,7 +318,7 @@ module Decode = let versionDecoder: (YAMLiciousTypes.YAMLElement -> string) = Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) - let classDecoder: (YAMLiciousTypes.YAMLElement -> Class) = + let classDecoder: (YAMLiciousTypes.YAMLElement -> CWLClass) = Decode.object (fun get -> match get.Required.Field "class" Decode.string with | "Workflow" -> Workflow diff --git a/tests/CWL/ARCtrl.CWL.Tests.fsproj b/tests/CWL/ARCtrl.CWL.Tests.fsproj index 586424c8..495bc135 100644 --- a/tests/CWL/ARCtrl.CWL.Tests.fsproj +++ b/tests/CWL/ARCtrl.CWL.Tests.fsproj @@ -8,7 +8,6 @@ - diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 197e24d4..5f472922 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -5,17 +5,22 @@ open ARCtrl.CWL.CWLTypes open ARCtrl.CWL.Requirements open ARCtrl.CWL.Inputs open ARCtrl.CWL.Outputs -open YAMLicious open TestingUtils +open DynamicObj let decodeCWLToolDescription = - TestObjects.CWL.CommandLineTool.cwl + TestObjects.CWL.CommandLineTool.cwlFile |> Decode.decodeCommandLineTool +let decodeCWLToolDescriptionMetadata = + TestObjects.CWL.CommandLineToolMetadata.cwlFile + |> Decode.decodeCommandLineTool + + let testCWLToolDescription = - testList "CWLToolDescription" [ + testList "Decode" [ testCase "Class" <| fun _ -> - let expected = Class.CommandLineTool + let expected = CWLClass.CommandLineTool let actual = decodeCWLToolDescription.Class Expect.isTrue (expected = actual) @@ -170,4 +175,170 @@ let testCWLToolDescription = Expect.isTrue (expected = actual) $"Expected: {expected}\nActual: {actual}" + ] + +let testCWLToolDescriptionMetadata = + testList "Decode with Metadata" [ + testCase "Class" <| fun _ -> + let expected = CWLClass.CommandLineTool + let actual = decodeCWLToolDescriptionMetadata.Class + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "CWLVersion" <| fun _ -> + let expected = "v1.2" + let actual = decodeCWLToolDescriptionMetadata.CWLVersion + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "baseCommand" <| fun _ -> + let expected = Some [|"dotnet"; "fsi"; "script.fsx"|] + let actual = decodeCWLToolDescriptionMetadata.BaseCommand + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + 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.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "Requirements" [ + let requirementsItem = decodeCWLToolDescriptionMetadata.Requirements + testCase "InitialWorkDirRequirement" <| fun _ -> + let expected = InitialWorkDirRequirement [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|] + let actual = requirementsItem.Value.[0] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "EnvVarRequirement" <| fun _ -> + let expected = EnvVarRequirement [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|] + let actual = requirementsItem.Value.[1] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "NetworkAccessRequirement" <| fun _ -> + let expected = NetworkAccessRequirement + let actual = requirementsItem.Value.[2] + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "Inputs" [ + let inputsItem = decodeCWLToolDescriptionMetadata.Inputs.Value + testCase "Length" <| fun _ -> + let expected = 2 + let actual = inputsItem.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testList "File" [ + let fileItem = inputsItem.[0] + testCase "Name" <| fun _ -> + let expected = "firstArg" + let actual = fileItem.Name + Expect.isTrue + ("firstArg" = fileItem.Name) + "Name of input is not 'firstArg'" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type_.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} + let actual = fileItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "String" [ + let stringItem = inputsItem.[1] + testCase "Name" <| fun _ -> + let expected = "secondArg" + let actual = stringItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = String + let actual = stringItem.Type_.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "InputBinding" <| fun _ -> + let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} + let actual = stringItem.InputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] + testList "Outputs" [ + let outputsItem = decodeCWLToolDescriptionMetadata.Outputs + testCase "Length" <| fun _ -> + let expected = 2 + let actual = outputsItem.Length + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testList "Directory" [ + let directoryItem = outputsItem.[0] + testCase "Name" <| fun _ -> + let expected = "output" + let actual = directoryItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = Directory (DirectoryInstance()) + let actual = directoryItem.Type_.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"} + let actual = directoryItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + testList "File" [ + let fileItem = outputsItem.[1] + testCase "Name" <| fun _ -> + let expected = "output2" + let actual = fileItem.Name + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "Type" <| fun _ -> + let expected = File (FileInstance()) + let actual = fileItem.Type_.Value + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + testCase "OutputBinding" <| fun _ -> + let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"} + let actual = fileItem.OutputBinding + Expect.isTrue + (expected = actual) + $"Expected: {expected}\nActual: {actual}" + ] + ] + 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 + $"Expected: {expected}\nActual: {actual}" + ] + +let main = + testList "CWLToolDescription" [ + testCWLToolDescription + testCWLToolDescriptionMetadata ] \ No newline at end of file diff --git a/tests/CWL/CWLObjectMetadata.Tests.fs b/tests/CWL/CWLObjectMetadata.Tests.fs deleted file mode 100644 index a913bc06..00000000 --- a/tests/CWL/CWLObjectMetadata.Tests.fs +++ /dev/null @@ -1,174 +0,0 @@ -module Tests.CWLObjectMetadata - -open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs -open DynamicObj -open YAMLicious -open TestingUtils - -let decodeCWLToolDescription = - TestObjects.CWL.CommandLineToolMetadata.cwl - |> Decode.decodeCommandLineTool - -let testCWLToolDescriptionMetadata = - testList "CWLToolDescription" [ - testCase "Class" <| fun _ -> - let expected = Class.CommandLineTool - let actual = decodeCWLToolDescription.Class - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "CWLVersion" <| fun _ -> - let expected = "v1.2" - let actual = decodeCWLToolDescription.CWLVersion - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "baseCommand" <| fun _ -> - let expected = Some [|"dotnet"; "fsi"; "script.fsx"|] - let actual = decodeCWLToolDescription.BaseCommand - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "Requirements" [ - let requirementsItem = decodeCWLToolDescription.Requirements - testCase "InitialWorkDirRequirement" <| fun _ -> - let expected = InitialWorkDirRequirement [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|] - let actual = requirementsItem.Value.[0] - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "EnvVarRequirement" <| fun _ -> - let expected = EnvVarRequirement [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|] - let actual = requirementsItem.Value.[1] - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "NetworkAccessRequirement" <| fun _ -> - let expected = NetworkAccessRequirement - let actual = requirementsItem.Value.[2] - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "Inputs" [ - let inputsItem = decodeCWLToolDescription.Inputs.Value - testCase "Length" <| fun _ -> - let expected = 2 - let actual = inputsItem.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testList "File" [ - let fileItem = inputsItem.[0] - testCase "Name" <| fun _ -> - let expected = "firstArg" - let actual = fileItem.Name - Expect.isTrue - ("firstArg" = fileItem.Name) - "Name of input is not 'firstArg'" - testCase "Type" <| fun _ -> - let expected = File (FileInstance()) - let actual = fileItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "InputBinding" <| fun _ -> - let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} - let actual = fileItem.InputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "String" [ - let stringItem = inputsItem.[1] - testCase "Name" <| fun _ -> - let expected = "secondArg" - let actual = stringItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "Type" <| fun _ -> - let expected = String - let actual = stringItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "InputBinding" <| fun _ -> - let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} - let actual = stringItem.InputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - ] - testList "Outputs" [ - let outputsItem = decodeCWLToolDescription.Outputs - testCase "Length" <| fun _ -> - let expected = 2 - let actual = outputsItem.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testList "Directory" [ - let directoryItem = outputsItem.[0] - testCase "Name" <| fun _ -> - let expected = "output" - let actual = directoryItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "Type" <| fun _ -> - let expected = Directory (DirectoryInstance()) - let actual = directoryItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "OutputBinding" <| fun _ -> - let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"} - let actual = directoryItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - testList "File" [ - let fileItem = outputsItem.[1] - testCase "Name" <| fun _ -> - let expected = "output2" - let actual = fileItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "Type" <| fun _ -> - let expected = File (FileInstance()) - let actual = fileItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - testCase "OutputBinding" <| fun _ -> - let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"} - let actual = fileItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" - ] - ] - testCase "Metadata" <| fun _ -> - Expect.isSome decodeCWLToolDescription.Metadata $"Expected {decodeCWLToolDescription.Metadata} to be Some" - let expected = TestObjects.CWL.CommandLineToolMetadata.expectedMetadataString.Trim().Replace("\r\n", "\n") - let actual = (decodeCWLToolDescription.Metadata.Value |> DynObj.format).Trim().Replace("\r\n", "\n") - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" - ] \ No newline at end of file diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs index a603181b..5df8c33f 100644 --- a/tests/CWL/CWLWorkflow.Tests.fs +++ b/tests/CWL/CWLWorkflow.Tests.fs @@ -7,17 +7,16 @@ open ARCtrl.CWL.Inputs open ARCtrl.CWL.Outputs open ARCtrl.CWL.Outputs.Workflow open ARCtrl.CWL.Inputs.Workflow -open YAMLicious open TestingUtils let decodeCWLWorkflowDescription = - TestObjects.CWL.Workflow.workflow + TestObjects.CWL.Workflow.workflowFile |> Decode.decodeWorkflow let testCWLWorkflowDescription = - testList "CWLWorkflowDescription" [ + testList "Decode" [ testCase "Class" <| fun _ -> - let expected = Class.Workflow + let expected = CWLClass.Workflow let actual = decodeCWLWorkflowDescription.Class Expect.equal actual expected $"Expected: {expected}\nActual: {actual}" @@ -130,4 +129,9 @@ let testCWLWorkflowDescription = Expect.equal a.OutputBinding e.OutputBinding $"Expected: {e}\nActual: {a}" ) actual expected + ] + +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 index 92afcc37..285265c6 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -7,13 +7,13 @@ open YAMLicious open TestingUtils let decodeInput = - TestObjects.CWL.Inputs.inputs + TestObjects.CWL.Inputs.inputsFileContent |> Decode.read |> Decode.inputsDecoder |>fun i ->i.Value let testInput = - testList "inputs with Directory, File and string" [ + testList "Decode" [ testCase "Length" <| fun _ -> Expect.isTrue (3 = decodeInput.Length) "Length of inputs is not 3" testList "Directory" [ let directoryItem = decodeInput.[0] @@ -42,4 +42,9 @@ let testInput = (expected = actual) $"Expected: {expected}\nActual: {actual}" ] + ] + +let main = + testList "Input" [ + testInput ] \ No newline at end of file diff --git a/tests/CWL/Main.fs b/tests/CWL/Main.fs index 7083fc19..c08a8276 100644 --- a/tests/CWL/Main.fs +++ b/tests/CWL/Main.fs @@ -3,14 +3,13 @@ module ARCtrl.CWL.Tests open Fable.Pyxpecto let all = testSequenced <| testList "CWL" [ - Tests.CWLWorkflow.testCWLWorkflowDescription - Tests.CWLObject.testCWLToolDescription - Tests.CWLObjectMetadata.testCWLToolDescriptionMetadata - Tests.Metadata.testMetadata - Tests.Outputs.testOutput - Tests.Inputs.testInput - Tests.Requirements.testRequirement - Tests.WorkflowSteps.testWorkflowStep + Tests.CWLWorkflow.main + Tests.CWLObject.main + Tests.Metadata.main + Tests.Outputs.main + Tests.Inputs.main + Tests.Requirements.main + Tests.WorkflowSteps.main ] #if !TESTS_ALL diff --git a/tests/CWL/Metadata.Tests.fs b/tests/CWL/Metadata.Tests.fs index 602376bd..1c5faf2b 100644 --- a/tests/CWL/Metadata.Tests.fs +++ b/tests/CWL/Metadata.Tests.fs @@ -1,17 +1,12 @@ module Tests.Metadata -open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs open ARCtrl.CWL.Decode open DynamicObj open YAMLicious open TestingUtils let decodeMetadata = - TestObjects.CWL.Metadata.metadata + TestObjects.CWL.Metadata.metadataFileContent |> Decode.read let overflowDictionary = @@ -22,7 +17,7 @@ let dynObj = overflowDecoder (new DynamicObj()) overflowDictionary let testMetadata = - testList "CWL Metadata" [ + 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 @@ -38,4 +33,10 @@ let testMetadata = let actualValue = dynObj |> DynObj.tryGetTypedPropertyValue "arc:technology platform" Expect.equal actualValue.Value expectedValue $"Expected: {expectedValue}\nActual: {actualValue}" + ] + + +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 index be3d161a..499a23d6 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -7,12 +7,12 @@ open YAMLicious open TestingUtils let decodeOutput = - TestObjects.CWL.Outputs.outputs + TestObjects.CWL.Outputs.outputsFileContent |> Decode.read |> Decode.outputsDecoder let testOutput = - testList "outputs with basetypes and array" [ + testList "Decode" [ testCase "Length" <| fun _ -> let expected = 5 let actual = decodeOutput.Length @@ -124,4 +124,9 @@ let testOutput = (expected = actual) $"Expected: {expected}\nActual: {actual}" ] + ] + +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 index f2ec2731..e94d8868 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -7,13 +7,13 @@ open YAMLicious open TestingUtils let decodeRequirement = - TestObjects.CWL.Requirements.requirements + TestObjects.CWL.Requirements.requirementsFileContent |> Decode.read |> Decode.requirementsDecoder |> fun r -> r.Value let testRequirement = - testList "requirements with DockerRequirement, InitialWorkDirRequirement, EnvVarRequirement and NetworkAccess" [ + testList "Decode" [ testCase "Length" <| fun _ -> Expect.isTrue (5 = decodeRequirement.Length) "Length of requirements is not 5" testList "DockerRequirement" [ let dockerItem = decodeRequirement.[0] @@ -60,4 +60,9 @@ let testRequirement = (expected = actual) $"Expected: {expected}\nActual: {actual}" ] + ] + +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 index 94bbc4b0..8948add5 100644 --- a/tests/CWL/WorkflowSteps.Tests.fs +++ b/tests/CWL/WorkflowSteps.Tests.fs @@ -9,12 +9,12 @@ open YAMLicious open TestingUtils let decodeWorkflowStep = - TestObjects.CWL.WorkflowSteps.workflowSteps + TestObjects.CWL.WorkflowSteps.workflowStepsFileContent |> Decode.read |> Decode.stepsDecoder let testWorkflowStep = - testList "Decode WorkflowStep" [ + testList "Decode" [ testCase "Length" <| fun _ -> Expect.isTrue (2 = decodeWorkflowStep.Length) "Length of WorkflowSteps is not 2" testList "IDs" [ testCase "MzMLToMzlite" <| fun _ -> @@ -79,4 +79,9 @@ let testWorkflowStep = (expected = actual) $"Expected: {expected}\nActual: {actual}" ] + ] + +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 index ac2b6d0f..9593a0bc 100644 --- a/tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs +++ b/tests/TestingUtils/TestObjects.CWL/CommandLineTool.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.CommandLineTool -let cwl ="""cwlVersion: v1.2 +let cwlFile ="""cwlVersion: v1.2 class: CommandLineTool hints: - class: DockerRequirement diff --git a/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs b/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs index ce1e581c..5e0ba034 100644 --- a/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs +++ b/tests/TestingUtils/TestObjects.CWL/CommandLineToolMetadata.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.CommandLineToolMetadata -let cwl ="""cwlVersion: v1.2 +let cwlFile ="""cwlVersion: v1.2 class: CommandLineTool hints: - class: DockerRequirement diff --git a/tests/TestingUtils/TestObjects.CWL/Inputs.fs b/tests/TestingUtils/TestObjects.CWL/Inputs.fs index 907617af..34024a3f 100644 --- a/tests/TestingUtils/TestObjects.CWL/Inputs.fs +++ b/tests/TestingUtils/TestObjects.CWL/Inputs.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.Inputs -let inputs ="""inputs: +let inputsFileContent ="""inputs: arcDirectory: Directory firstArg: type: File diff --git a/tests/TestingUtils/TestObjects.CWL/Metadata.fs b/tests/TestingUtils/TestObjects.CWL/Metadata.fs index 733084f2..6b0827ef 100644 --- a/tests/TestingUtils/TestObjects.CWL/Metadata.fs +++ b/tests/TestingUtils/TestObjects.CWL/Metadata.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.Metadata -let metadata ="""arc:has technology type: +let metadataFileContent ="""arc:has technology type: - class: arc:technology type arc:annotation value: "Fsharp Devcontainer" diff --git a/tests/TestingUtils/TestObjects.CWL/Outputs.fs b/tests/TestingUtils/TestObjects.CWL/Outputs.fs index 8f7f1ad3..93311e79 100644 --- a/tests/TestingUtils/TestObjects.CWL/Outputs.fs +++ b/tests/TestingUtils/TestObjects.CWL/Outputs.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.Outputs -let outputs ="""outputs: +let outputsFileContent ="""outputs: output: type: File outputBinding: diff --git a/tests/TestingUtils/TestObjects.CWL/Requirements.fs b/tests/TestingUtils/TestObjects.CWL/Requirements.fs index a909ada4..3191e8b8 100644 --- a/tests/TestingUtils/TestObjects.CWL/Requirements.fs +++ b/tests/TestingUtils/TestObjects.CWL/Requirements.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.Requirements -let requirements ="""requirements: +let requirementsFileContent ="""requirements: - class: DockerRequirement dockerImageId: "devcontainer" dockerFile: {$include: "FSharpArcCapsule/Dockerfile"} diff --git a/tests/TestingUtils/TestObjects.CWL/Workflow.fs b/tests/TestingUtils/TestObjects.CWL/Workflow.fs index 4845c65a..e082d3bd 100644 --- a/tests/TestingUtils/TestObjects.CWL/Workflow.fs +++ b/tests/TestingUtils/TestObjects.CWL/Workflow.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.Workflow -let workflow ="""cwlVersion: v1.2 +let workflowFile ="""cwlVersion: v1.2 class: Workflow requirements: diff --git a/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs b/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs index 32d2f492..f9a8d6c7 100644 --- a/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs +++ b/tests/TestingUtils/TestObjects.CWL/WorkflowSteps.fs @@ -1,6 +1,6 @@ module TestObjects.CWL.WorkflowSteps -let workflowSteps ="""steps: +let workflowStepsFileContent ="""steps: MzMLToMzlite: run: ./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl in: From e6d9902b573d1c3271957cf20877b5475e2b3024 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 18 Oct 2024 13:52:13 +0200 Subject: [PATCH 42/49] add attachmembers attribute --- src/CWL/CWL.fs | 3 +++ src/CWL/Inputs.fs | 2 ++ src/CWL/Outputs.fs | 2 ++ src/CWL/WorkflowSteps.fs | 2 ++ 4 files changed, 9 insertions(+) diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index 9d1b2a5c..c4ea8751 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -7,9 +7,11 @@ open Requirements open Inputs open Outputs open WorkflowSteps +open Fable.Core module CWLProcessingUnits = + [] type CWLToolDescription ( cwlVersion: string, cls: CWLClass, @@ -63,6 +65,7 @@ module CWLProcessingUnits = with get() = _metadata and set(metadata) = _metadata <- metadata + [] type CWLWorkflowDescription( cwlVersion: string, cls: CWLClass, diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 360dbcef..2b688414 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -3,6 +3,7 @@ namespace ARCtrl.CWL open CWLTypes open Outputs.Workflow open DynamicObj +open Fable.Core module Inputs = @@ -13,6 +14,7 @@ module Inputs = Separate: bool option } + [] type Input ( name: string, ?type_: CWLType, diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index 034b488f..75f2c503 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -2,6 +2,7 @@ namespace ARCtrl.CWL open CWLTypes open DynamicObj +open Fable.Core module Outputs = @@ -9,6 +10,7 @@ module Outputs = Glob: string option } + [] type Output ( name: string, ?type_: CWLType, diff --git a/src/CWL/WorkflowSteps.fs b/src/CWL/WorkflowSteps.fs index 775d7f34..c1b72a70 100644 --- a/src/CWL/WorkflowSteps.fs +++ b/src/CWL/WorkflowSteps.fs @@ -5,9 +5,11 @@ open Outputs.Workflow open Inputs.Workflow open Requirements open DynamicObj +open Fable.Core module WorkflowSteps = + [] type WorkflowStep ( id: string, in_: StepInput [], From 070ca96665fbbe241b379b64ae8576f89c7808f9 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 18 Oct 2024 20:04:55 +0200 Subject: [PATCH 43/49] switch to resizearray and add some comments --- src/CWL/CWL.fs | 40 +++--- src/CWL/CWLTypes.fs | 10 +- src/CWL/Decode.fs | 106 +++++++++++----- src/CWL/Outputs.fs | 2 +- src/CWL/Requirements.fs | 38 +++++- src/CWL/WorkflowSteps.fs | 12 +- tests/CWL/CWLObject.Tests.fs | 207 ++++++++++--------------------- tests/CWL/CWLWorkflow.Tests.fs | 69 ++++------- tests/CWL/Inputs.Tests.fs | 22 ++-- tests/CWL/Metadata.Tests.fs | 9 +- tests/CWL/Outputs.Tests.fs | 66 +++------- tests/CWL/Requirements.Tests.fs | 39 +++--- tests/CWL/WorkflowSteps.Tests.fs | 42 ++----- 13 files changed, 292 insertions(+), 370 deletions(-) diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs index c4ea8751..2d1a5553 100644 --- a/src/CWL/CWL.fs +++ b/src/CWL/CWL.fs @@ -15,22 +15,22 @@ module CWLProcessingUnits = type CWLToolDescription ( cwlVersion: string, cls: CWLClass, - outputs: Output [], - ?baseCommand: string [], - ?requirements: Requirement [], - ?hints: Requirement [], - ?inputs: Input [], + outputs: ResizeArray, + ?baseCommand: ResizeArray, + ?requirements: ResizeArray, + ?hints: ResizeArray, + ?inputs: ResizeArray, ?metadata: DynamicObj ) = inherit DynamicObj () let mutable _cwlVersion: string = cwlVersion let mutable _class: CWLClass = cls - let mutable _outputs: Output [] = outputs - let mutable _baseCommand: string [] option = baseCommand - let mutable _requirements: Requirement [] option = requirements - let mutable _hints: Requirement [] option = hints - let mutable _inputs: Input [] option = inputs + 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 @@ -69,22 +69,22 @@ module CWLProcessingUnits = type CWLWorkflowDescription( cwlVersion: string, cls: CWLClass, - steps: WorkflowStep [], - inputs: Input [], - outputs: Output [], - ?requirements: Requirement [], - ?hints: Requirement [], + steps: ResizeArray, + inputs: ResizeArray, + outputs: ResizeArray, + ?requirements: ResizeArray, + ?hints: ResizeArray, ?metadata: DynamicObj ) = inherit DynamicObj() let mutable _cwlVersion: string = cwlVersion let mutable _class: CWLClass = cls - let mutable _steps: WorkflowStep [] = steps - let mutable _inputs: Input [] = inputs - let mutable _outputs: Output [] = outputs - let mutable _requirements: Requirement [] option = requirements - let mutable _hints: Requirement [] option = hints + 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 diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs index 7a40590c..ee7cd991 100644 --- a/src/CWL/CWLTypes.fs +++ b/src/CWL/CWLTypes.fs @@ -17,9 +17,15 @@ module CWLTypes = 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 @@ -52,6 +58,6 @@ module CWLTypes = type SoftwarePackage = { Package: string - Version: string [] option - Specs: string [] option + Version: ResizeArray option + Specs: ResizeArray option } diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 2e3dbd3c..ea4f8690 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -12,8 +12,17 @@ open Outputs.Workflow open WorkflowSteps 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 @@ -31,24 +40,28 @@ module Decode = | _ -> 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 @@ -60,6 +73,7 @@ module Decode = } ) + /// 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 @@ -76,6 +90,7 @@ module Decode = | _ -> failwith "Invalid CWL type" ) + /// Match the input string to the possible CWL types let cwlTypeStringMatcher t (get: Decode.IGetters) = match t with | "File" -> File (FileInstance ()) @@ -100,6 +115,7 @@ module Decode = | "null" -> Null | _ -> failwith "Invalid CWL type" + /// Access the type field and decode a YAMLElement into a CWLType let cwlTypeDecoder: (YAMLiciousTypes.YAMLElement -> CWLType) = Decode.object (fun get -> let cwlType = @@ -120,7 +136,8 @@ module Decode = cwlTypeArray ) - let outputArrayDecoder: (YAMLiciousTypes.YAMLElement -> Output[]) = + /// Decode a YAMLElement into an Output Array + let outputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = Decode.object (fun get -> let dict = get.Overflow.FieldList [] [| @@ -142,15 +159,18 @@ module Decode = if outputSource.IsSome then DynObj.setOptionalProperty "outputSource" outputSource output output - |] + |] + |> ResizeArray ) - - let outputsDecoder: (YAMLiciousTypes.YAMLElement -> Output[]) = + + /// 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 @@ -159,12 +179,13 @@ module Decode = } dockerReq - let envVarRequirementDecoder (get: Decode.IGetters): EnvironmentDef[] = + /// Decode a YAMLElement into an EnvVarRequirement array + let envVarRequirementDecoder (get: Decode.IGetters): ResizeArray = let envDef = get.Required.Field "envDef" ( - Decode.array + Decode.resizearray ( Decode.object (fun get2 -> { @@ -176,32 +197,35 @@ module Decode = ) envDef - let softwareRequirementDecoder (get: Decode.IGetters): SoftwarePackage[] = + /// Decode a YAMLElement into a SoftwareRequirement array + let softwareRequirementDecoder (get: Decode.IGetters): ResizeArray = let envDef = get.Required.Field "packages" ( - Decode.array + Decode.resizearray ( Decode.object (fun get2 -> { Package = get2.Required.Field "package" Decode.string - Version = get2.Optional.Field "version" (Decode.array Decode.string) - Specs = get2.Optional.Field "specs" (Decode.array Decode.string) + Version = get2.Optional.Field "version" (Decode.resizearray Decode.string) + Specs = get2.Optional.Field "specs" (Decode.resizearray Decode.string) } ) ) ) envDef - let initialWorkDirRequirementDecoder (get: Decode.IGetters): CWLType[] = + /// 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.array direntDecoder) + (Decode.resizearray direntDecoder) initialWorkDir + /// Decode a YAMLElement into a ResourceRequirementInstance let resourceRequirementDecoder (get: Decode.IGetters): ResourceRequirementInstance = ResourceRequirementInstance( get.Optional.Field "coresMin" id, @@ -214,23 +238,27 @@ module Decode = get.Optional.Field "outdirMax" id ) - - let schemaDefRequirementDecoder (get: Decode.IGetters): SchemaDefRequirementType[] = + /// Decode a YAMLElement into a SchemaDefRequirementType array + let schemaDefRequirementDecoder (get: Decode.IGetters): ResizeArray = let schemaDef = get.Required.Field "types" ( - Decode.array + Decode.resizearray ( Decode.map id Decode.string ) ) - |> Array.map (fun m -> SchemaDefRequirementType(m.Keys |> Seq.item 0, m.Values |> Seq.item 0)) + |> 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 - let requirementArrayDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[]) = - Decode.array + /// 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 @@ -243,7 +271,10 @@ module Decode = | "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 @@ -252,18 +283,21 @@ module Decode = ) ) - let requirementsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[] option) = + /// 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 ) - let hintsDecoder: (YAMLiciousTypes.YAMLElement -> Requirement[] option) = + /// 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 = @@ -282,7 +316,8 @@ module Decode = outputBinding ) - let inputArrayDecoder: (YAMLiciousTypes.YAMLElement -> Input[]) = + /// Decode a YAMLElement into an Input array + let inputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = Decode.object (fun get -> let dict = get.Overflow.FieldList [] [| @@ -301,18 +336,20 @@ module Decode = if inputBinding.IsSome then DynObj.setOptionalProperty "inputBinding" inputBinding input input - |] + |] + |> ResizeArray ) - - let inputsDecoder: (YAMLiciousTypes.YAMLElement -> Input[] option) = + + /// 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 -> string [] option) = + let baseCommandDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray option) = Decode.object (fun get -> - get.Optional.Field "baseCommand" (Decode.array Decode.string) + get.Optional.Field "baseCommand" (Decode.resizearray Decode.string) ) let versionDecoder: (YAMLiciousTypes.YAMLElement -> string) = @@ -338,7 +375,7 @@ module Decode = fieldValue ) - let inputStepDecoder: (YAMLiciousTypes.YAMLElement -> StepInput []) = + let inputStepDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = Decode.object (fun get -> let dict = get.Overflow.FieldList [] [| @@ -357,12 +394,13 @@ module Decode = let defaultValue = stringOptionFieldDecoder "default" value let valueFrom = stringOptionFieldDecoder "valueFrom" value { Id = key; Source = source; DefaultValue = defaultValue; ValueFrom = valueFrom } - |] + |] + |> ResizeArray ) - let outputStepsDecoder: (YAMLiciousTypes.YAMLElement -> string []) = + let outputStepsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = Decode.object (fun get -> - let outputs = get.Required.Field "out" (Decode.array Decode.string) + let outputs = get.Required.Field "out" (Decode.resizearray Decode.string) outputs ) @@ -389,7 +427,8 @@ module Decode = if hints.IsSome then wfStep.Hints <- hints wfStep - |] + |] + |> ResizeArray ) let stepsDecoder = @@ -398,6 +437,7 @@ module Decode = 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 @@ -472,6 +512,7 @@ module Decode = 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 @@ -483,9 +524,6 @@ module Decode = let requirements = requirementsDecoder yamlCWL let hints = hintsDecoder yamlCWL let steps = stepsDecoder yamlCWL - printfn "%A" steps - printfn "%A" outputs - printfn "%A" inputs let description = CWLProcessingUnits.CWLWorkflowDescription( cwlVersion, diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index 75f2c503..997bf967 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -29,5 +29,5 @@ module Outputs = module Workflow = type StepOutput = { - Id: string [] + Id: ResizeArray } \ No newline at end of file diff --git a/src/CWL/Requirements.fs b/src/CWL/Requirements.fs index 00a6503a..b6362680 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -11,12 +11,19 @@ module Requirements = 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, @@ -38,18 +45,37 @@ module Requirements = DynObj.setOptionalProperty (nameof outdirMin) outdirMin this DynObj.setOptionalProperty (nameof outdirMax) outdirMax this - type Requirement = + type Requirement = + /// Indicates that the workflow platform must support inline Javascript expressions. | InlineJavascriptRequirement - | SchemaDefRequirement of SchemaDefRequirementType [] + /// 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 - | SoftwareRequirement of SoftwarePackage [] - | InitialWorkDirRequirement of CWLType [] - | EnvVarRequirement of EnvironmentDef [] + /// 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/WorkflowSteps.fs b/src/CWL/WorkflowSteps.fs index c1b72a70..54fd70df 100644 --- a/src/CWL/WorkflowSteps.fs +++ b/src/CWL/WorkflowSteps.fs @@ -12,20 +12,20 @@ module WorkflowSteps = [] type WorkflowStep ( id: string, - in_: StepInput [], + in_: ResizeArray, out_: StepOutput, run: string, - ?requirements: Requirement [], - ?hints: Requirement [] + ?requirements: ResizeArray, + ?hints: ResizeArray ) = inherit DynamicObj () let mutable _id: string = id - let mutable _in: StepInput [] = in_ + let mutable _in: ResizeArray = in_ let mutable _out: StepOutput = out_ let mutable _run: string = run - let mutable _requirements: Requirement [] option = requirements - let mutable _hints: Requirement [] option = hints + let mutable _requirements: ResizeArray option = requirements + let mutable _hints: ResizeArray option = hints member this.Id with get() = _id diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 5f472922..3c356ed7 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -22,159 +22,121 @@ let testCWLToolDescription = testCase "Class" <| fun _ -> let expected = CWLClass.CommandLineTool let actual = decodeCWLToolDescription.Class - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "CWLVersion" <| fun _ -> let expected = "v1.2" let actual = decodeCWLToolDescription.CWLVersion - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "baseCommand" <| fun _ -> - let expected = Some [|"dotnet"; "fsi"; "script.fsx"|] + let expected = Some (ResizeArray [|"dotnet"; "fsi"; "script.fsx"|]) let actual = decodeCWLToolDescription.BaseCommand - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "Requirements" [ let requirementsItem = decodeCWLToolDescription.Requirements testCase "InitialWorkDirRequirement" <| fun _ -> - let expected = InitialWorkDirRequirement [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|] + let expected = InitialWorkDirRequirement (ResizeArray [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|]) let actual = requirementsItem.Value.[0] - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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 [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|] + let expected = EnvVarRequirement (ResizeArray [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|]) let actual = requirementsItem.Value.[1] - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "Inputs" [ let inputsItem = decodeCWLToolDescription.Inputs.Value testCase "Length" <| fun _ -> let expected = 2 - let actual = inputsItem.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - ("firstArg" = fileItem.Name) - "Name of input is not 'firstArg'" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = File (FileInstance()) let actual = fileItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "InputBinding" <| fun _ -> let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} let actual = fileItem.InputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "String" [ let stringItem = inputsItem.[1] testCase "Name" <| fun _ -> let expected = "secondArg" let actual = stringItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = String let actual = stringItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "InputBinding" <| fun _ -> let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} let actual = stringItem.InputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] ] testList "Outputs" [ let outputsItem = decodeCWLToolDescription.Outputs testCase "Length" <| fun _ -> let expected = 2 - let actual = outputsItem.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) let actual = directoryItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"} let actual = directoryItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "File" [ let fileItem = outputsItem.[1] testCase "Name" <| fun _ -> let expected = "output2" let actual = fileItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = File (FileInstance()) let actual = fileItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"} let actual = fileItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] ] testCase "Metadata" <| fun _ -> let expected = None let actual = decodeCWLToolDescription.Metadata - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] let testCWLToolDescriptionMetadata = @@ -182,159 +144,122 @@ let testCWLToolDescriptionMetadata = testCase "Class" <| fun _ -> let expected = CWLClass.CommandLineTool let actual = decodeCWLToolDescriptionMetadata.Class - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "CWLVersion" <| fun _ -> let expected = "v1.2" let actual = decodeCWLToolDescriptionMetadata.CWLVersion - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "baseCommand" <| fun _ -> - let expected = Some [|"dotnet"; "fsi"; "script.fsx"|] + let expected = Some (ResizeArray [|"dotnet"; "fsi"; "script.fsx"|]) let actual = decodeCWLToolDescriptionMetadata.BaseCommand - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "Requirements" [ let requirementsItem = decodeCWLToolDescriptionMetadata.Requirements testCase "InitialWorkDirRequirement" <| fun _ -> - let expected = InitialWorkDirRequirement [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|] + let expected = InitialWorkDirRequirement (ResizeArray [|Dirent {Entry = "$include: script.fsx"; Entryname = Some "script.fsx"; Writable = None }|]) let actual = requirementsItem.Value.[0] - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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 [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|] + let expected = EnvVarRequirement (ResizeArray [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}|]) let actual = requirementsItem.Value.[1] - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "Inputs" [ let inputsItem = decodeCWLToolDescriptionMetadata.Inputs.Value testCase "Length" <| fun _ -> let expected = 2 - let actual = inputsItem.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - ("firstArg" = fileItem.Name) - "Name of input is not 'firstArg'" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = File (FileInstance()) let actual = fileItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "InputBinding" <| fun _ -> let expected = Some {Position = Some 1; Prefix = None; ItemSeparator = None; Separate = None} let actual = fileItem.InputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "String" [ let stringItem = inputsItem.[1] testCase "Name" <| fun _ -> let expected = "secondArg" let actual = stringItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = String let actual = stringItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "InputBinding" <| fun _ -> let expected = Some {Position = Some 2; Prefix = None; ItemSeparator = None; Separate = None} let actual = stringItem.InputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] ] testList "Outputs" [ let outputsItem = decodeCWLToolDescriptionMetadata.Outputs testCase "Length" <| fun _ -> let expected = 2 - let actual = outputsItem.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) let actual = directoryItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "$(runtime.outdir)/.nuget"} let actual = directoryItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "File" [ let fileItem = outputsItem.[1] testCase "Name" <| fun _ -> let expected = "output2" let actual = fileItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = File (FileInstance()) let actual = fileItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "$(runtime.outdir)/*.csv"} let actual = fileItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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 - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] let main = diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs index 5df8c33f..5b9d63c7 100644 --- a/tests/CWL/CWLWorkflow.Tests.fs +++ b/tests/CWL/CWLWorkflow.Tests.fs @@ -18,21 +18,18 @@ let testCWLWorkflowDescription = testCase "Class" <| fun _ -> let expected = CWLClass.Workflow let actual = decodeCWLWorkflowDescription.Class - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "CWLVersion" <| fun _ -> let expected = "v1.2" let actual = decodeCWLWorkflowDescription.CWLVersion - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "MultipleInputFeatureRequirement" <| fun _ -> let requirementsItem = decodeCWLWorkflowDescription.Requirements let expected = MultipleInputFeatureRequirement let actual = requirementsItem.Value.[0] - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "inputs" <| fun _ -> - let expected = [| + let expected = ResizeArray [| Input("cores", CWLType.Int); Input("db", CWLType.File (FileInstance())); Input ("stage", CWLType.Directory (DirectoryInstance())); @@ -43,43 +40,35 @@ let testCWLWorkflowDescription = Input ("paramsPSM", CWLType.File (FileInstance())) |] let actual = decodeCWLWorkflowDescription.Inputs - Array.iter2 (fun (a: Input) (e: Input) -> - Expect.equal a.Name e.Name - $"Expected: {e}\nActual: {a}" - Expect.equal a.InputBinding e.InputBinding - $"Expected: {e}\nActual: {a}" - Expect.equal a.Type_ e.Type_ - $"Expected: {e}\nActual: {a}" - ) actual expected + 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 - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "PeptideSpectrumMatching" <| fun _ -> let expected = "PeptideSpectrumMatching" let actual = workflowSteps.[1].Id - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" + 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 - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "PeptideSpectrumMatching" <| fun _ -> let expected = "./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl" let actual = workflowSteps.[1].Run - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "In" [ testCase "MzMLToMzlite" <| fun _ -> - let expected = [| + 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}; @@ -87,10 +76,9 @@ let testCWLWorkflowDescription = {Id = "parallelismLevel"; Source = Some "cores"; DefaultValue = None; ValueFrom = None} |] let actual = workflowSteps.[0].In - Expect.equal actual expected - $"Expected:\n{expected}\nActual:\n{actual}" + Expect.sequenceEqual actual expected "" testCase "PeptideSpectrumMatching" <| fun _ -> - let expected = [| + 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}; @@ -99,36 +87,29 @@ let testCWLWorkflowDescription = {Id = "parallelismLevel"; Source = Some "cores"; DefaultValue = None; ValueFrom = None}; |] let actual = workflowSteps.[1].In - Expect.equal actual expected - $"Expected:\n{expected}\nActual:\n{actual}" + Expect.sequenceEqual actual expected "" ] testList "Out" [ testCase "MzMLToMzlite" <| fun _ -> - let expected = {Id = [|"dir"|]} + let expected = {Id = ResizeArray [|"dir"|]} let actual = workflowSteps.[0].Out - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" + Expect.sequenceEqual actual.Id expected.Id "" testCase "PeptideSpectrumMatching" <| fun _ -> - let expected = {Id = [|"dir"|]} + let expected = {Id = ResizeArray [|"dir"|]} let actual = workflowSteps.[1].Out - Expect.equal actual expected - $"Expected: {expected}\nActual: {actual}" + Expect.sequenceEqual actual.Id expected.Id "" ] ] testCase "outputs" <| fun _ -> - let expected = [| + let expected = ResizeArray [| Output("mzlite", CWLType.Directory (DirectoryInstance()), outputSource = "MzMLToMzlite/dir"); Output("psm", CWLType.Directory (DirectoryInstance()), outputSource = "PeptideSpectrumMatching/dir") |] let actual = decodeCWLWorkflowDescription.Outputs - Array.iter2 (fun (a: Output) (e: Output) -> - Expect.equal a.Name e.Name - $"Expected: {e}\nActual: {a}" - Expect.equal a.Type_ e.Type_ - $"Expected: {e}\nActual: {a}" - Expect.equal a.OutputBinding e.OutputBinding - $"Expected: {e}\nActual: {a}" - ) actual expected + 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 = diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs index 285265c6..4176c706 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -14,33 +14,29 @@ let decodeInput = let testInput = testList "Decode" [ - testCase "Length" <| fun _ -> Expect.isTrue (3 = decodeInput.Length) "Length of inputs is not 3" + testCase "Length" <| fun _ -> Expect.equal 3 decodeInput.Count "" testList "Directory" [ let directoryItem = decodeInput.[0] - testCase "Name" <| fun _ -> Expect.isTrue ("arcDirectory" = directoryItem.Name) "Name of input is not 'arcDirectory'" - testCase "Type" <| fun _ -> Expect.isTrue ((Directory (DirectoryInstance())) = directoryItem.Type_.Value) "Type of input is not Directory" + 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.isTrue ("firstArg" = fileItem.Name) "Name of input is not 'firstArg'" - testCase "Type" <| fun _ -> Expect.isTrue ((File (FileInstance())) = fileItem.Type_.Value) "Type of input is not File" - testCase "InputBinding" <| fun _ -> Expect.isTrue (Some {Position = Some 1; Prefix = Some "--example"; ItemSeparator = None; Separate = None} = fileItem.InputBinding) "InputBinding of input is not Some Pattern" + 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 "String" [ let stringItem = decodeInput.[2] - testCase "Name" <| fun _ -> Expect.isTrue ("secondArg" = stringItem.Name) "Name of input is not 'secondArg'" + testCase "Name" <| fun _ -> Expect.equal "secondArg" stringItem.Name "" testCase "Type" <| fun _ -> let expected = String let actual = stringItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] ] diff --git a/tests/CWL/Metadata.Tests.fs b/tests/CWL/Metadata.Tests.fs index 1c5faf2b..53fe2e69 100644 --- a/tests/CWL/Metadata.Tests.fs +++ b/tests/CWL/Metadata.Tests.fs @@ -21,18 +21,15 @@ let testMetadata = 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 - $"Expected: {expected}\nActual: {actual}" + 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 - $"Expected: {expected}\nActual: {actual}" + 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 - $"Expected: {expectedValue}\nActual: {actualValue}" + Expect.equal actualValue.Value expectedValue "" ] diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs index 499a23d6..89cd212e 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -15,114 +15,82 @@ let testOutput = testList "Decode" [ testCase "Length" <| fun _ -> let expected = 5 - let actual = decodeOutput.Length - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = File (FileInstance()) let actual = fileItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "./arc/runs/fsResult1/result.csv"} let actual = fileItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "Directory" [ let directoryItem = decodeOutput.[1] testCase "Name" <| fun _ -> let expected = "example1" let actual = directoryItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) let actual = directoryItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} let actual = directoryItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "Directory 2" [ let directoryItem = decodeOutput.[2] testCase "Name" <| fun _ -> let expected = "example2" let actual = directoryItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = Directory (DirectoryInstance()) let actual = directoryItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = None let actual = directoryItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "File Array" [ let fileArrayItem = decodeOutput.[3] testCase "Name" <| fun _ -> let expected = "exampleArray1" let actual = fileArrayItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = Array (File (FileInstance())) let actual = fileArrayItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} let actual = fileArrayItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "File Array 2" [ let fileArrayItem = decodeOutput.[4] testCase "Name" <| fun _ -> let expected = "exampleArray2" let actual = fileArrayItem.Name - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "Type" <| fun _ -> let expected = Array (File (FileInstance())) let actual = fileArrayItem.Type_.Value - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "OutputBinding" <| fun _ -> let expected = Some {Glob = Some "./arc/runs/fsResult1/example.csv"} let actual = fileArrayItem.OutputBinding - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] ] diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index e94d8868..30107dfa 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -14,51 +14,52 @@ let decodeRequirement = let testRequirement = testList "Decode" [ - testCase "Length" <| fun _ -> Expect.isTrue (5 = decodeRequirement.Length) "Length of requirements is not 5" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "InitialWorkDirRequirement" [ let initialWorkDirItem = decodeRequirement.[1] testCase "Class" <| fun _ -> - let expected = InitialWorkDirRequirement [|Dirent {Entryname = Some "arc"; Entry = "$(inputs.arcDirectory)"; Writable = Some true}; Dirent {Entryname = None; Entry = "$(inputs.outputDirectory)"; Writable = Some true}|] + 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 - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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 [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}; {EnvName = "TEST"; EnvValue = "false"}|] + let expected = EnvVarRequirement (ResizeArray [|{EnvName = "DOTNET_NOLOGO"; EnvValue = "true"}; {EnvName = "TEST"; EnvValue = "false"}|]) let actual = envVarItem - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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 [|{Package = "interproscan"; Specs = Some [| "https://identifiers.org/rrid/RRID:SCR_005829" |]; Version = Some [| "5.21-60" |]}|] + 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 - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + 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.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] ] diff --git a/tests/CWL/WorkflowSteps.Tests.fs b/tests/CWL/WorkflowSteps.Tests.fs index 8948add5..2519be31 100644 --- a/tests/CWL/WorkflowSteps.Tests.fs +++ b/tests/CWL/WorkflowSteps.Tests.fs @@ -15,69 +15,53 @@ let decodeWorkflowStep = let testWorkflowStep = testList "Decode" [ - testCase "Length" <| fun _ -> Expect.isTrue (2 = decodeWorkflowStep.Length) "Length of WorkflowSteps is not 2" + testCase "Length" <| fun _ -> Expect.equal 2 decodeWorkflowStep.Count "" testList "IDs" [ testCase "MzMLToMzlite" <| fun _ -> let expected = "MzMLToMzlite" let actual = decodeWorkflowStep.[0].Id - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "PeptideSpectrumMatching" <| fun _ -> let expected = "PeptideSpectrumMatching" let actual = decodeWorkflowStep.[1].Id - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "Run" [ testCase "MzMLToMzlite" <| fun _ -> let expected = "./runs/MzMLToMzlite/proteomiqon-mzmltomzlite.cwl" let actual = decodeWorkflowStep.[0].Run - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" testCase "PeptideSpectrumMatching" <| fun _ -> let expected = "./runs/PeptideSpectrumMatching/proteomiqon-peptidespectrummatching.cwl" let actual = decodeWorkflowStep.[1].Run - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.equal actual expected "" ] testList "In" [ testCase "MzMLToMzlite" <| fun _ -> - let expected = [| + 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.isTrue - (expected = actual) - $"Expected:\n{expected.[0]}\n{expected.[1]}\nActual:\n{actual.[0]}\n{actual.[1]}" + Expect.sequenceEqual actual expected "" testCase "PeptideSpectrumMatching" <| fun _ -> - let expected = [| + 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.isTrue - (expected = actual) - $"Expected:\n{expected.[0]}\n{expected.[1]}\n{expected.[2]}\n{expected.[3]}\nActual:\n{actual.[0]}\n{actual.[1]}\n{actual.[2]}\n{actual.[3]}" + Expect.sequenceEqual actual expected "" ] testList "Out" [ testCase "MzMLToMzlite" <| fun _ -> - let expected = {Id = [|"dir"|]} + let expected = {Id = ResizeArray [|"dir"|]} let actual = decodeWorkflowStep.[0].Out - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.sequenceEqual actual.Id expected.Id "" testCase "PeptideSpectrumMatching" <| fun _ -> - let expected = {Id = [|"dir1";"dir2"|]} + let expected = {Id = ResizeArray [|"dir1";"dir2"|]} let actual = decodeWorkflowStep.[1].Out - Expect.isTrue - (expected = actual) - $"Expected: {expected}\nActual: {actual}" + Expect.sequenceEqual actual.Id expected.Id "" ] ] From 090a21479fed7a818dde644b1fcbeb42ade796fc Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Fri, 18 Oct 2024 20:49:00 +0200 Subject: [PATCH 44/49] add optional field to inputs --- src/CWL/Decode.fs | 24 +++++++++++++------- src/CWL/Inputs.fs | 5 +++- tests/CWL/Inputs.Tests.fs | 16 +++++++++++-- tests/TestingUtils/TestObjects.CWL/Inputs.fs | 3 +++ 4 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index ea4f8690..9d17d0f2 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -90,9 +90,14 @@ module Decode = | _ -> failwith "Invalid CWL type" ) - /// Match the input string to the possible CWL types - let cwlTypeStringMatcher t (get: Decode.IGetters) = - match t with + /// 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) @@ -114,9 +119,10 @@ module Decode = | "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) = + let cwlTypeDecoder: (YAMLiciousTypes.YAMLElement -> CWLType*bool) = Decode.object (fun get -> let cwlType = get.Required.Field @@ -133,7 +139,7 @@ module Decode = cwlTypeStringMatcher t get | None -> let cwlTypeArray = get.Required.Field "type" cwlArrayTypeDecoder - cwlTypeArray + cwlTypeArray, false ) /// Decode a YAMLElement into an Output Array @@ -147,8 +153,8 @@ module Decode = let outputSource = get.Optional.Field "outputSource" Decode.string let cwlType = match value with - | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get - | _ -> cwlTypeDecoder value + | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get |> fst + | _ -> cwlTypeDecoder value |> fst let output = Output( key, @@ -324,7 +330,7 @@ module Decode = for key in dict.Keys do let value = dict.[key] let inputBinding = inputBindingDecoder value - let cwlType = + let cwlType,optional = match value with | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get | _ -> cwlTypeDecoder value @@ -335,6 +341,8 @@ module Decode = ) if inputBinding.IsSome then DynObj.setOptionalProperty "inputBinding" inputBinding input + if optional then + DynObj.setOptionalProperty "optional" (Some true) input input |] |> ResizeArray diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 2b688414..67ee36d1 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -18,15 +18,18 @@ module Inputs = type Input ( name: string, ?type_: CWLType, - ?inputBinding: InputBinding + ?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 module Workflow = diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs index 4176c706..ddf7c545 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -14,7 +14,7 @@ let decodeInput = let testInput = testList "Decode" [ - testCase "Length" <| fun _ -> Expect.equal 3 decodeInput.Count "" + testCase "Length" <| fun _ -> Expect.equal 5 decodeInput.Count "" testList "Directory" [ let directoryItem = decodeInput.[0] testCase "Name" <| fun _ -> Expect.equal "arcDirectory" directoryItem.Name "" @@ -26,8 +26,20 @@ let testInput = 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.[2] + let stringItem = decodeInput.[4] testCase "Name" <| fun _ -> Expect.equal "secondArg" stringItem.Name "" testCase "Type" <| fun _ -> let expected = String diff --git a/tests/TestingUtils/TestObjects.CWL/Inputs.fs b/tests/TestingUtils/TestObjects.CWL/Inputs.fs index 34024a3f..c040a6ce 100644 --- a/tests/TestingUtils/TestObjects.CWL/Inputs.fs +++ b/tests/TestingUtils/TestObjects.CWL/Inputs.fs @@ -7,6 +7,9 @@ let inputsFileContent ="""inputs: inputBinding: position: 1 prefix: --example + argOptional: + type: File? + argOptionalMap: File[]? secondArg: type: string inputBinding: From 10e40baef1e30f56bce21a3d1cfb9b7874436667 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 21 Oct 2024 10:53:22 +0200 Subject: [PATCH 45/49] separate processing units and remove module --- src/CWL/ARCtrl.CWL.fsproj | 7 +- src/CWL/CWL.fs | 120 --------------------------------- src/CWL/Decode.fs | 4 +- src/CWL/ToolDescription.fs | 63 +++++++++++++++++ src/CWL/WorkflowDescription.fs | 64 ++++++++++++++++++ tests/CWL/CWLObject.Tests.fs | 4 +- tests/CWL/CWLWorkflow.Tests.fs | 2 +- 7 files changed, 136 insertions(+), 128 deletions(-) delete mode 100644 src/CWL/CWL.fs create mode 100644 src/CWL/ToolDescription.fs create mode 100644 src/CWL/WorkflowDescription.fs diff --git a/src/CWL/ARCtrl.CWL.fsproj b/src/CWL/ARCtrl.CWL.fsproj index 1a98eda5..52968dfa 100644 --- a/src/CWL/ARCtrl.CWL.fsproj +++ b/src/CWL/ARCtrl.CWL.fsproj @@ -13,12 +13,13 @@ - + + - - + + diff --git a/src/CWL/CWL.fs b/src/CWL/CWL.fs deleted file mode 100644 index 2d1a5553..00000000 --- a/src/CWL/CWL.fs +++ /dev/null @@ -1,120 +0,0 @@ -namespace ARCtrl - -open ARCtrl.CWL -open DynamicObj -open CWLTypes -open Requirements -open Inputs -open Outputs -open WorkflowSteps -open Fable.Core - -module CWLProcessingUnits = - - [] - type CWLToolDescription ( - cwlVersion: string, - cls: CWLClass, - outputs: ResizeArray, - ?baseCommand: ResizeArray, - ?requirements: ResizeArray, - ?hints: ResizeArray, - ?inputs: ResizeArray, - ?metadata: DynamicObj - ) = - inherit DynamicObj () - - let mutable _cwlVersion: string = cwlVersion - let mutable _class: CWLClass = cls - 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.Class - with get() = _class - and set(cls) = _class <- cls - - 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 - - [] - type CWLWorkflowDescription( - cwlVersion: string, - cls: CWLClass, - steps: ResizeArray, - inputs: ResizeArray, - outputs: ResizeArray, - ?requirements: ResizeArray, - ?hints: ResizeArray, - ?metadata: DynamicObj - ) = - inherit DynamicObj() - - let mutable _cwlVersion: string = cwlVersion - let mutable _class: CWLClass = cls - 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.Class - with get() = _class - and set(cls) = _class <- cls - - 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/Decode.fs b/src/CWL/Decode.fs index 9d17d0f2..929d8056 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -455,7 +455,7 @@ module Decode = let hints = hintsDecoder yamlCWL let baseCommand = baseCommandDecoder yamlCWL let description = - CWLProcessingUnits.CWLToolDescription( + CWLToolDescription( cwlVersion, CommandLineTool, outputs @@ -533,7 +533,7 @@ module Decode = let hints = hintsDecoder yamlCWL let steps = stepsDecoder yamlCWL let description = - CWLProcessingUnits.CWLWorkflowDescription( + CWLWorkflowDescription( cwlVersion, Workflow, steps, diff --git a/src/CWL/ToolDescription.fs b/src/CWL/ToolDescription.fs new file mode 100644 index 00000000..30aa490c --- /dev/null +++ b/src/CWL/ToolDescription.fs @@ -0,0 +1,63 @@ +namespace ARCtrl.CWL + +open ARCtrl.CWL +open DynamicObj +open CWLTypes +open Requirements +open Inputs +open Outputs +open Fable.Core + +[] +type CWLToolDescription ( + cwlVersion: string, + cls: CWLClass, + outputs: ResizeArray, + ?baseCommand: ResizeArray, + ?requirements: ResizeArray, + ?hints: ResizeArray, + ?inputs: ResizeArray, + ?metadata: DynamicObj + ) = + inherit DynamicObj () + + let mutable _cwlVersion: string = cwlVersion + let mutable _class: CWLClass = cls + 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.Class + with get() = _class + and set(cls) = _class <- cls + + 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..7ff7d2e0 --- /dev/null +++ b/src/CWL/WorkflowDescription.fs @@ -0,0 +1,64 @@ +namespace ARCtrl.CWL + +open ARCtrl.CWL +open DynamicObj +open CWLTypes +open Requirements +open Inputs +open Outputs +open WorkflowSteps +open Fable.Core + +[] +type CWLWorkflowDescription( + cwlVersion: string, + cls: CWLClass, + steps: ResizeArray, + inputs: ResizeArray, + outputs: ResizeArray, + ?requirements: ResizeArray, + ?hints: ResizeArray, + ?metadata: DynamicObj +) = + inherit DynamicObj() + + let mutable _cwlVersion: string = cwlVersion + let mutable _class: CWLClass = cls + 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.Class + with get() = _class + and set(cls) = _class <- cls + + 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/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 3c356ed7..4ffc91ec 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -8,11 +8,11 @@ open ARCtrl.CWL.Outputs open TestingUtils open DynamicObj -let decodeCWLToolDescription = +let decodeCWLToolDescription: CWLToolDescription = TestObjects.CWL.CommandLineTool.cwlFile |> Decode.decodeCommandLineTool -let decodeCWLToolDescriptionMetadata = +let decodeCWLToolDescriptionMetadata: CWLToolDescription = TestObjects.CWL.CommandLineToolMetadata.cwlFile |> Decode.decodeCommandLineTool diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs index 5b9d63c7..c0d74d94 100644 --- a/tests/CWL/CWLWorkflow.Tests.fs +++ b/tests/CWL/CWLWorkflow.Tests.fs @@ -9,7 +9,7 @@ open ARCtrl.CWL.Outputs.Workflow open ARCtrl.CWL.Inputs.Workflow open TestingUtils -let decodeCWLWorkflowDescription = +let decodeCWLWorkflowDescription: CWLWorkflowDescription = TestObjects.CWL.Workflow.workflowFile |> Decode.decodeWorkflow From de6ef63c19bace7c567691d8f074923dabb5cba1 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 21 Oct 2024 11:01:10 +0200 Subject: [PATCH 46/49] change version and class to optional on tool and wf descriptions --- src/CWL/Decode.fs | 10 ++++------ src/CWL/ToolDescription.fs | 8 ++++---- src/CWL/WorkflowDescription.fs | 8 ++++---- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index 929d8056..dfdbfe97 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -456,9 +456,8 @@ module Decode = let baseCommand = baseCommandDecoder yamlCWL let description = CWLToolDescription( - cwlVersion, - CommandLineTool, - outputs + outputs, + cwlVersion ) let metadata = let md = new DynamicObj () @@ -534,11 +533,10 @@ module Decode = let steps = stepsDecoder yamlCWL let description = CWLWorkflowDescription( - cwlVersion, - Workflow, steps, inputs, - outputs + outputs, + cwlVersion ) let metadata = let md = new DynamicObj () diff --git a/src/CWL/ToolDescription.fs b/src/CWL/ToolDescription.fs index 30aa490c..8db98bc7 100644 --- a/src/CWL/ToolDescription.fs +++ b/src/CWL/ToolDescription.fs @@ -10,9 +10,9 @@ open Fable.Core [] type CWLToolDescription ( - cwlVersion: string, - cls: CWLClass, outputs: ResizeArray, + ?cwlVersion: string, + ?cls: CWLClass, ?baseCommand: ResizeArray, ?requirements: ResizeArray, ?hints: ResizeArray, @@ -21,8 +21,8 @@ type CWLToolDescription ( ) = inherit DynamicObj () - let mutable _cwlVersion: string = cwlVersion - let mutable _class: CWLClass = cls + let mutable _cwlVersion: string = cwlVersion |> Option.defaultValue "v1.2" + let mutable _class: CWLClass = cls |> Option.defaultValue CWLClass.CommandLineTool let mutable _outputs: ResizeArray = outputs let mutable _baseCommand: ResizeArray option = baseCommand let mutable _requirements: ResizeArray option = requirements diff --git a/src/CWL/WorkflowDescription.fs b/src/CWL/WorkflowDescription.fs index 7ff7d2e0..1625c759 100644 --- a/src/CWL/WorkflowDescription.fs +++ b/src/CWL/WorkflowDescription.fs @@ -11,19 +11,19 @@ open Fable.Core [] type CWLWorkflowDescription( - cwlVersion: string, - cls: CWLClass, steps: ResizeArray, inputs: ResizeArray, outputs: ResizeArray, + ?cwlVersion: string, + ?cls: CWLClass, ?requirements: ResizeArray, ?hints: ResizeArray, ?metadata: DynamicObj ) = inherit DynamicObj() - let mutable _cwlVersion: string = cwlVersion - let mutable _class: CWLClass = cls + let mutable _cwlVersion: string = cwlVersion |> Option.defaultValue "v1.2" + let mutable _class: CWLClass = cls |> Option.defaultValue CWLClass.Workflow let mutable _steps: ResizeArray = steps let mutable _inputs: ResizeArray = inputs let mutable _outputs: ResizeArray = outputs From b7837828e800818dbd91f884ebcf7a48b988e409 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 21 Oct 2024 11:20:17 +0200 Subject: [PATCH 47/49] remove type CLWClass :( --- src/CWL/CWLTypes.fs | 5 ----- src/CWL/Decode.fs | 8 ++------ src/CWL/ToolDescription.fs | 6 ------ src/CWL/WorkflowDescription.fs | 6 ------ tests/CWL/CWLObject.Tests.fs | 4 ---- tests/CWL/CWLWorkflow.Tests.fs | 4 ---- 6 files changed, 2 insertions(+), 31 deletions(-) diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs index ee7cd991..ff9ae7eb 100644 --- a/src/CWL/CWLTypes.fs +++ b/src/CWL/CWLTypes.fs @@ -37,11 +37,6 @@ module CWLTypes = | Null | Array of CWLType - type CWLClass = - | Workflow - | CommandLineTool - | ExpressionTool - type InputRecordSchema () = inherit DynamicObj () diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index dfdbfe97..ee90a09b 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -363,13 +363,9 @@ module Decode = let versionDecoder: (YAMLiciousTypes.YAMLElement -> string) = Decode.object (fun get -> get.Required.Field "cwlVersion" Decode.string) - let classDecoder: (YAMLiciousTypes.YAMLElement -> CWLClass) = + let classDecoder: (YAMLiciousTypes.YAMLElement -> string) = Decode.object (fun get -> - match get.Required.Field "class" Decode.string with - | "Workflow" -> Workflow - | "CommandLineTool" -> CommandLineTool - | "ExpressionTool" -> ExpressionTool - | _ -> failwith "Invalid class" + get.Required.Field "class" Decode.string ) let stringOptionFieldDecoder field : (YAMLiciousTypes.YAMLElement -> string option) = Decode.object(fun get -> diff --git a/src/CWL/ToolDescription.fs b/src/CWL/ToolDescription.fs index 8db98bc7..3bf046ce 100644 --- a/src/CWL/ToolDescription.fs +++ b/src/CWL/ToolDescription.fs @@ -12,7 +12,6 @@ open Fable.Core type CWLToolDescription ( outputs: ResizeArray, ?cwlVersion: string, - ?cls: CWLClass, ?baseCommand: ResizeArray, ?requirements: ResizeArray, ?hints: ResizeArray, @@ -22,7 +21,6 @@ type CWLToolDescription ( inherit DynamicObj () let mutable _cwlVersion: string = cwlVersion |> Option.defaultValue "v1.2" - let mutable _class: CWLClass = cls |> Option.defaultValue CWLClass.CommandLineTool let mutable _outputs: ResizeArray = outputs let mutable _baseCommand: ResizeArray option = baseCommand let mutable _requirements: ResizeArray option = requirements @@ -34,10 +32,6 @@ type CWLToolDescription ( with get() = _cwlVersion and set(version) = _cwlVersion <- version - member this.Class - with get() = _class - and set(cls) = _class <- cls - member this.Outputs with get() = _outputs and set(outputs) = _outputs <- outputs diff --git a/src/CWL/WorkflowDescription.fs b/src/CWL/WorkflowDescription.fs index 1625c759..26416e52 100644 --- a/src/CWL/WorkflowDescription.fs +++ b/src/CWL/WorkflowDescription.fs @@ -15,7 +15,6 @@ type CWLWorkflowDescription( inputs: ResizeArray, outputs: ResizeArray, ?cwlVersion: string, - ?cls: CWLClass, ?requirements: ResizeArray, ?hints: ResizeArray, ?metadata: DynamicObj @@ -23,7 +22,6 @@ type CWLWorkflowDescription( inherit DynamicObj() let mutable _cwlVersion: string = cwlVersion |> Option.defaultValue "v1.2" - let mutable _class: CWLClass = cls |> Option.defaultValue CWLClass.Workflow let mutable _steps: ResizeArray = steps let mutable _inputs: ResizeArray = inputs let mutable _outputs: ResizeArray = outputs @@ -35,10 +33,6 @@ type CWLWorkflowDescription( with get() = _cwlVersion and set(version) = _cwlVersion <- version - member this.Class - with get() = _class - and set(cls) = _class <- cls - member this.Steps with get() = _steps and set(steps) = _steps <- steps diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 4ffc91ec..7c456527 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -19,10 +19,6 @@ let decodeCWLToolDescriptionMetadata: CWLToolDescription = let testCWLToolDescription = testList "Decode" [ - testCase "Class" <| fun _ -> - let expected = CWLClass.CommandLineTool - let actual = decodeCWLToolDescription.Class - Expect.equal actual expected "" testCase "CWLVersion" <| fun _ -> let expected = "v1.2" let actual = decodeCWLToolDescription.CWLVersion diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs index c0d74d94..8f8852f3 100644 --- a/tests/CWL/CWLWorkflow.Tests.fs +++ b/tests/CWL/CWLWorkflow.Tests.fs @@ -15,10 +15,6 @@ let decodeCWLWorkflowDescription: CWLWorkflowDescription = let testCWLWorkflowDescription = testList "Decode" [ - testCase "Class" <| fun _ -> - let expected = CWLClass.Workflow - let actual = decodeCWLWorkflowDescription.Class - Expect.equal actual expected "" testCase "CWLVersion" <| fun _ -> let expected = "v1.2" let actual = decodeCWLWorkflowDescription.CWLVersion From fedc6d4a7198db9bc2bbfa749f0965f27d1fbf3b Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 21 Oct 2024 11:33:22 +0200 Subject: [PATCH 48/49] reorganize modules --- src/CWL/Decode.fs | 2 -- src/CWL/Inputs.fs | 10 ---------- src/CWL/Outputs.fs | 9 ++------- src/CWL/ToolDescription.fs | 1 - src/CWL/WorkflowSteps.fs | 17 +++++++++++++++-- tests/CWL/CWLObject.Tests.fs | 4 ---- tests/CWL/CWLWorkflow.Tests.fs | 3 +-- tests/CWL/WorkflowSteps.Tests.fs | 3 --- 8 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/CWL/Decode.fs b/src/CWL/Decode.fs index ee90a09b..236fb5b6 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -6,9 +6,7 @@ open YAMLicious.YAMLiciousTypes open CWLTypes open Requirements open Inputs -open Inputs.Workflow open Outputs -open Outputs.Workflow open WorkflowSteps open DynamicObj diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 67ee36d1..542e368b 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -1,7 +1,6 @@ namespace ARCtrl.CWL open CWLTypes -open Outputs.Workflow open DynamicObj open Fable.Core @@ -30,12 +29,3 @@ module Inputs = member this.Type_ = DynObj.tryGetTypedPropertyValue ("type") this member this.InputBinding = DynObj.tryGetTypedPropertyValue ("inputBinding") this member this.Optional = DynObj.tryGetTypedPropertyValue ("optional") this - - module Workflow = - - type StepInput = { - Id: string - Source: string option - DefaultValue: string option - ValueFrom: string option - } diff --git a/src/CWL/Outputs.fs b/src/CWL/Outputs.fs index 997bf967..0eea41f2 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -21,13 +21,8 @@ module Outputs = 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 - - module Workflow = - - type StepOutput = { - Id: ResizeArray - } \ No newline at end of file + member this.OutputSource = DynObj.tryGetTypedPropertyValue ("outputSource") this \ No newline at end of file diff --git a/src/CWL/ToolDescription.fs b/src/CWL/ToolDescription.fs index 3bf046ce..1f7582cc 100644 --- a/src/CWL/ToolDescription.fs +++ b/src/CWL/ToolDescription.fs @@ -2,7 +2,6 @@ namespace ARCtrl.CWL open ARCtrl.CWL open DynamicObj -open CWLTypes open Requirements open Inputs open Outputs diff --git a/src/CWL/WorkflowSteps.fs b/src/CWL/WorkflowSteps.fs index 54fd70df..9d827b80 100644 --- a/src/CWL/WorkflowSteps.fs +++ b/src/CWL/WorkflowSteps.fs @@ -1,14 +1,25 @@ namespace ARCtrl.CWL open CWLTypes -open Outputs.Workflow -open Inputs.Workflow +open Outputs +open Inputs open Requirements open DynamicObj open Fable.Core module WorkflowSteps = + type StepInput = { + Id: string + Source: string option + DefaultValue: string option + ValueFrom: string option + } + + type StepOutput = { + Id: ResizeArray + } + [] type WorkflowStep ( id: string, @@ -50,3 +61,5 @@ module WorkflowSteps = member this.Hints with get() = _hints and set(hints) = _hints <- hints + + diff --git a/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index 7c456527..c09a8981 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -137,10 +137,6 @@ let testCWLToolDescription = let testCWLToolDescriptionMetadata = testList "Decode with Metadata" [ - testCase "Class" <| fun _ -> - let expected = CWLClass.CommandLineTool - let actual = decodeCWLToolDescriptionMetadata.Class - Expect.equal actual expected "" testCase "CWLVersion" <| fun _ -> let expected = "v1.2" let actual = decodeCWLToolDescriptionMetadata.CWLVersion diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs index 8f8852f3..7cb83ba3 100644 --- a/tests/CWL/CWLWorkflow.Tests.fs +++ b/tests/CWL/CWLWorkflow.Tests.fs @@ -5,8 +5,7 @@ open ARCtrl.CWL.CWLTypes open ARCtrl.CWL.Requirements open ARCtrl.CWL.Inputs open ARCtrl.CWL.Outputs -open ARCtrl.CWL.Outputs.Workflow -open ARCtrl.CWL.Inputs.Workflow +open ARCtrl.CWL.WorkflowSteps open TestingUtils let decodeCWLWorkflowDescription: CWLWorkflowDescription = diff --git a/tests/CWL/WorkflowSteps.Tests.fs b/tests/CWL/WorkflowSteps.Tests.fs index 2519be31..d8883046 100644 --- a/tests/CWL/WorkflowSteps.Tests.fs +++ b/tests/CWL/WorkflowSteps.Tests.fs @@ -1,10 +1,7 @@ module Tests.WorkflowSteps open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes open ARCtrl.CWL.WorkflowSteps -open ARCtrl.CWL.Outputs.Workflow -open ARCtrl.CWL.Inputs.Workflow open YAMLicious open TestingUtils From 844ca8be60907c50ffaaeb427642240a70b96958 Mon Sep 17 00:00:00 2001 From: Caroline Ott Date: Mon, 21 Oct 2024 15:44:52 +0200 Subject: [PATCH 49/49] remove underlying modules --- src/CWL/CWLTypes.fs | 106 +++++++++++------------ src/CWL/Decode.fs | 18 ++-- src/CWL/Inputs.fs | 47 +++++----- src/CWL/Outputs.fs | 41 ++++----- src/CWL/Requirements.fs | 143 +++++++++++++++---------------- src/CWL/ToolDescription.fs | 12 +-- src/CWL/WorkflowDescription.fs | 13 +-- src/CWL/WorkflowSteps.fs | 110 +++++++++++------------- tests/CWL/CWLObject.Tests.fs | 4 - tests/CWL/CWLWorkflow.Tests.fs | 25 +++--- tests/CWL/Inputs.Tests.fs | 2 - tests/CWL/Outputs.Tests.fs | 2 - tests/CWL/Requirements.Tests.fs | 2 - tests/CWL/WorkflowSteps.Tests.fs | 1 - 14 files changed, 239 insertions(+), 287 deletions(-) diff --git a/src/CWL/CWLTypes.fs b/src/CWL/CWLTypes.fs index ff9ae7eb..0a148161 100644 --- a/src/CWL/CWLTypes.fs +++ b/src/CWL/CWLTypes.fs @@ -2,57 +2,55 @@ namespace ARCtrl.CWL open DynamicObj -module CWLTypes = - - 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 - } +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 index 236fb5b6..9cf05f14 100644 --- a/src/CWL/Decode.fs +++ b/src/CWL/Decode.fs @@ -1,13 +1,7 @@ namespace ARCtrl.CWL -open ARCtrl open YAMLicious open YAMLicious.YAMLiciousTypes -open CWLTypes -open Requirements -open Inputs -open Outputs -open WorkflowSteps open DynamicObj module ResizeArray = @@ -141,7 +135,7 @@ module Decode = ) /// Decode a YAMLElement into an Output Array - let outputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = + let outputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = Decode.object (fun get -> let dict = get.Overflow.FieldList [] [| @@ -154,7 +148,7 @@ module Decode = | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get |> fst | _ -> cwlTypeDecoder value |> fst let output = - Output( + CWLOutput( key, cwlType ) @@ -168,7 +162,7 @@ module Decode = ) /// Access the outputs field and decode a YAMLElement into an Output Array - let outputsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = + let outputsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = Decode.object (fun get -> let outputs = get.Required.Field "outputs" outputArrayDecoder outputs @@ -321,7 +315,7 @@ module Decode = ) /// Decode a YAMLElement into an Input array - let inputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = + let inputArrayDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray) = Decode.object (fun get -> let dict = get.Overflow.FieldList [] [| @@ -333,7 +327,7 @@ module Decode = | YAMLElement.Object [YAMLElement.Value v] -> cwlTypeStringMatcher v.Value get | _ -> cwlTypeDecoder value let input = - Input( + CWLInput( key, cwlType ) @@ -347,7 +341,7 @@ module Decode = ) /// Access the inputs field and decode the YAMLElements into an Input array - let inputsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray option) = + let inputsDecoder: (YAMLiciousTypes.YAMLElement -> ResizeArray option) = Decode.object (fun get -> let outputs = get.Optional.Field "inputs" inputArrayDecoder outputs diff --git a/src/CWL/Inputs.fs b/src/CWL/Inputs.fs index 542e368b..cdacb82f 100644 --- a/src/CWL/Inputs.fs +++ b/src/CWL/Inputs.fs @@ -1,31 +1,28 @@ namespace ARCtrl.CWL -open CWLTypes open DynamicObj open Fable.Core -module Inputs = +type InputBinding = { + Prefix: string option + Position: int option + ItemSeparator: string option + Separate: bool option +} - type InputBinding = { - Prefix: string option - Position: int option - ItemSeparator: string option - Separate: bool option - } - - [] - type Input ( - 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 +[] +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/Outputs.fs b/src/CWL/Outputs.fs index 0eea41f2..ae5dbf6c 100644 --- a/src/CWL/Outputs.fs +++ b/src/CWL/Outputs.fs @@ -1,28 +1,25 @@ namespace ARCtrl.CWL -open CWLTypes open DynamicObj open Fable.Core -module Outputs = +type OutputBinding = { + Glob: string option +} - type OutputBinding = { - Glob: string option - } - - [] - type Output ( - 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 +[] +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 index b6362680..fede905a 100644 --- a/src/CWL/Requirements.fs +++ b/src/CWL/Requirements.fs @@ -1,81 +1,78 @@ namespace ARCtrl.CWL open DynamicObj -open CWLTypes -module Requirements = +type DockerRequirement = { + DockerPull: string option + DockerFile: Map option + DockerImageId: string option +} - 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 +} - /// 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 - /// "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 +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 index 1f7582cc..ce2e8b7c 100644 --- a/src/CWL/ToolDescription.fs +++ b/src/CWL/ToolDescription.fs @@ -1,30 +1,26 @@ namespace ARCtrl.CWL -open ARCtrl.CWL open DynamicObj -open Requirements -open Inputs -open Outputs open Fable.Core [] type CWLToolDescription ( - outputs: ResizeArray, + outputs: ResizeArray, ?cwlVersion: string, ?baseCommand: ResizeArray, ?requirements: ResizeArray, ?hints: ResizeArray, - ?inputs: ResizeArray, + ?inputs: ResizeArray, ?metadata: DynamicObj ) = inherit DynamicObj () let mutable _cwlVersion: string = cwlVersion |> Option.defaultValue "v1.2" - let mutable _outputs: ResizeArray = outputs + 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 _inputs: ResizeArray option = inputs let mutable _metadata: DynamicObj option = metadata member this.CWLVersion diff --git a/src/CWL/WorkflowDescription.fs b/src/CWL/WorkflowDescription.fs index 26416e52..aa5664c1 100644 --- a/src/CWL/WorkflowDescription.fs +++ b/src/CWL/WorkflowDescription.fs @@ -2,18 +2,13 @@ namespace ARCtrl.CWL open ARCtrl.CWL open DynamicObj -open CWLTypes -open Requirements -open Inputs -open Outputs -open WorkflowSteps open Fable.Core [] type CWLWorkflowDescription( steps: ResizeArray, - inputs: ResizeArray, - outputs: ResizeArray, + inputs: ResizeArray, + outputs: ResizeArray, ?cwlVersion: string, ?requirements: ResizeArray, ?hints: ResizeArray, @@ -23,8 +18,8 @@ type CWLWorkflowDescription( 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 _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 diff --git a/src/CWL/WorkflowSteps.fs b/src/CWL/WorkflowSteps.fs index 9d827b80..3e4ae125 100644 --- a/src/CWL/WorkflowSteps.fs +++ b/src/CWL/WorkflowSteps.fs @@ -1,65 +1,59 @@ namespace ARCtrl.CWL -open CWLTypes -open Outputs -open Inputs -open Requirements open DynamicObj open Fable.Core -module WorkflowSteps = - - 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 +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/tests/CWL/CWLObject.Tests.fs b/tests/CWL/CWLObject.Tests.fs index c09a8981..238ab408 100644 --- a/tests/CWL/CWLObject.Tests.fs +++ b/tests/CWL/CWLObject.Tests.fs @@ -1,10 +1,6 @@ module Tests.CWLObject open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs open TestingUtils open DynamicObj diff --git a/tests/CWL/CWLWorkflow.Tests.fs b/tests/CWL/CWLWorkflow.Tests.fs index 7cb83ba3..da53ae8c 100644 --- a/tests/CWL/CWLWorkflow.Tests.fs +++ b/tests/CWL/CWLWorkflow.Tests.fs @@ -1,11 +1,6 @@ module Tests.CWLWorkflow open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements -open ARCtrl.CWL.Inputs -open ARCtrl.CWL.Outputs -open ARCtrl.CWL.WorkflowSteps open TestingUtils let decodeCWLWorkflowDescription: CWLWorkflowDescription = @@ -25,14 +20,14 @@ let testCWLWorkflowDescription = Expect.equal actual expected "" testCase "inputs" <| fun _ -> let expected = ResizeArray [| - Input("cores", CWLType.Int); - Input("db", CWLType.File (FileInstance())); - Input ("stage", CWLType.Directory (DirectoryInstance())); - Input ("outputMzML", CWLType.Directory (DirectoryInstance())); - Input ("outputPSM", CWLType.Directory (DirectoryInstance())); - Input ("inputMzML", CWLType.Directory (DirectoryInstance())); - Input ("paramsMzML", CWLType.File (FileInstance())); - Input ("paramsPSM", CWLType.File (FileInstance())) + 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 @@ -97,8 +92,8 @@ let testCWLWorkflowDescription = ] testCase "outputs" <| fun _ -> let expected = ResizeArray [| - Output("mzlite", CWLType.Directory (DirectoryInstance()), outputSource = "MzMLToMzlite/dir"); - Output("psm", CWLType.Directory (DirectoryInstance()), outputSource = "PeptideSpectrumMatching/dir") + 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 diff --git a/tests/CWL/Inputs.Tests.fs b/tests/CWL/Inputs.Tests.fs index ddf7c545..55baee87 100644 --- a/tests/CWL/Inputs.Tests.fs +++ b/tests/CWL/Inputs.Tests.fs @@ -1,8 +1,6 @@ module Tests.Inputs open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Inputs open YAMLicious open TestingUtils diff --git a/tests/CWL/Outputs.Tests.fs b/tests/CWL/Outputs.Tests.fs index 89cd212e..4fa6cb7a 100644 --- a/tests/CWL/Outputs.Tests.fs +++ b/tests/CWL/Outputs.Tests.fs @@ -1,8 +1,6 @@ module Tests.Outputs open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Outputs open YAMLicious open TestingUtils diff --git a/tests/CWL/Requirements.Tests.fs b/tests/CWL/Requirements.Tests.fs index 30107dfa..2ad6af5c 100644 --- a/tests/CWL/Requirements.Tests.fs +++ b/tests/CWL/Requirements.Tests.fs @@ -1,8 +1,6 @@ module Tests.Requirements open ARCtrl.CWL -open ARCtrl.CWL.CWLTypes -open ARCtrl.CWL.Requirements open YAMLicious open TestingUtils diff --git a/tests/CWL/WorkflowSteps.Tests.fs b/tests/CWL/WorkflowSteps.Tests.fs index d8883046..78219d2b 100644 --- a/tests/CWL/WorkflowSteps.Tests.fs +++ b/tests/CWL/WorkflowSteps.Tests.fs @@ -1,7 +1,6 @@ module Tests.WorkflowSteps open ARCtrl.CWL -open ARCtrl.CWL.WorkflowSteps open YAMLicious open TestingUtils