rbbt-util 5.32.26 → 5.32.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 072aa8b010c3f395051fd4e29f6ac22647944eec529bdf05be77ac6ad5260a37
4
- data.tar.gz: d68843b2fe56019dd63165a34c97ec23d9e60933e4493bdb61f29985548bca69
3
+ metadata.gz: ffa357551766aa6ae6da35c586949c0ff05814171304ca5a874689418c7c48e2
4
+ data.tar.gz: 5e1e419fcf846fc8ead12a77e6713f198a74d516c0262c34b4d86380a58af092
5
5
  SHA512:
6
- metadata.gz: 9d572a394b5736d8780021bc8ec65ba4381dc8cb84f111704854ce77ceca8da0ca72626e0144a04ab5c9b552a5566f4bdd553cad2a70050216fae231179beb95
7
- data.tar.gz: ce51f8a4f0f5b4028148deb7fd3e57a4fbf5bb531f366378a2bae31354b799a66c6ba2768d9ae25578b3a0b09ee4e29f817f6ab768166bed39e4245b35a4a7e4
6
+ metadata.gz: a6afd86e7698e5b7e4588412ed678497c825242616bc75d2fa2ab835bc325a8a58826da164dea16cd5b4bbf81697ac5201bb347af220bab5ce238f8e0ce4edcb
7
+ data.tar.gz: 3e86fe7c2d84cc7a04cdd348f03a6323b5467723182057ec028b5d611b3dd200fce41949f1502ca59b0a295b5664f1d6ba259d27a196039f873261e9faf7a1a6
@@ -51,6 +51,7 @@ export BATCH_SYSTEM=SLURM
51
51
  "nodes" => nodes,
52
52
  "time" => time,
53
53
  "exclusive" => exclusive,
54
+ "highmem" => highmem,
54
55
  "licenses" => licenses,
55
56
  "gres" => gres,
56
57
  "mem" => mem,
@@ -76,7 +76,7 @@ module Workflow
76
76
  extension :dep_task unless @extension
77
77
  returns workflow.tasks[oname].result_description if workflow.tasks.include?(oname) unless @result_description
78
78
  task name do
79
- raise RbbtException, "dependency not found in dep_task" if dependencies.empty?
79
+ raise RbbtException, "dep_task does not have any dependencies" if dependencies.empty?
80
80
  Step.wait_for_jobs dependencies.select{|d| d.streaming? }
81
81
  dep = dependencies.last
82
82
  dep.join
@@ -25,71 +25,6 @@ module Workflow
25
25
  end.compact
26
26
  end
27
27
 
28
- def self.load_inputs(dir, input_names, input_types)
29
- inputs = {}
30
- if File.exists?(dir) && ! File.directory?(dir)
31
- Log.debug "Loading inputs from #{dir}, not a directory trying as tar.gz"
32
- tarfile = dir
33
- digest = CMD.cmd("md5sum '#{tarfile}'").read.split(" ").first
34
- tmpdir = Rbbt.tmp.input_bundle[digest].find
35
- Misc.untar(tarfile, tmpdir) unless File.exists? tmpdir
36
- files = tmpdir.glob("*")
37
- if files.length == 1 && File.directory?(files.first)
38
- tmpdir = files.first
39
- end
40
- load_inputs(tmpdir, input_names, input_types)
41
- else
42
- dir = Path.setup(dir.dup)
43
- input_names.each do |input|
44
- file = dir[input].find
45
- file = dir.glob(input.to_s + ".*").reject{|f| f =~ /\.md5$/}.first if file.nil? or not file.exists?
46
- Log.debug "Trying #{ input }: #{file}"
47
- next unless file and file.exists?
48
-
49
-
50
- case input_types[input]
51
- when :file, :binary
52
- Log.debug "Pointing #{ input } to #{file}"
53
- if file =~ /\.yaml/
54
- inputs[input.to_sym] = YAML.load(Open.read(file))
55
- else
56
- if File.symlink?(file)
57
- link_target = File.expand_path(File.readlink(file), File.dirname(file))
58
- inputs[input.to_sym] = link_target
59
- else
60
- inputs[input.to_sym] = Open.realpath(file)
61
- end
62
- end
63
- when :text
64
- Log.debug "Reading #{ input } from #{file}"
65
- inputs[input.to_sym] = Open.read(file)
66
- when :array
67
- Log.debug "Reading array #{ input } from #{file}"
68
- inputs[input.to_sym] = Open.read(file).split("\n")
69
- when :tsv
70
- Log.debug "Opening tsv #{ input } from #{file}"
71
- inputs[input.to_sym] = TSV.open(file)
72
- when :boolean
73
- inputs[input.to_sym] = (file.read.strip == 'true')
74
- else
75
- Log.debug "Loading #{ input } from #{file}"
76
- inputs[input.to_sym] = file.read.strip
77
- end
78
-
79
- end
80
- inputs = IndiferentHash.setup(inputs)
81
-
82
- dir.glob("*#*").each do |od|
83
- name = File.basename(od)
84
- value = Open.read(od)
85
- Log.debug "Loading override dependency #{ name } as #{value}"
86
- inputs[name] = value.chomp
87
- end
88
-
89
- inputs
90
- end
91
- end
92
-
93
28
  def example_inputs(task_name, example)
94
29
  inputs = {}
95
30
  IndiferentHash.setup(inputs)
@@ -86,76 +86,6 @@ class Step
86
86
  end
87
87
  end
88
88
 
89
- def self.save_inputs(inputs, input_types, dir)
90
- inputs.each do |name,value|
91
- type = input_types[name]
92
- type = type.to_s if type
93
- path = File.join(dir, name.to_s)
94
-
95
- Log.debug "Saving job input #{name} (#{type}) into #{path}"
96
- case
97
- when Step === value
98
- Open.ln_s(value.path, path)
99
- when type.to_s == "file"
100
- if String === value && File.exists?(value)
101
- value = File.expand_path(value)
102
- Open.ln_s(value, path)
103
- else
104
- value = value.collect{|v| v = "#{v}" if Path === v; v }if Array === value
105
- value = "#{value}" if Path === value
106
- Open.write(path + '.yaml', value.to_yaml)
107
- end
108
- when Array === value
109
- Open.write(path, value.collect{|v| Step === v ? v.path : v.to_s} * "\n")
110
- when IO === value
111
- if value.filename && String === value.filename && File.exists?(value.filename)
112
- Open.ln_s(value.filename, path)
113
- else
114
- Open.write(path, value)
115
- end
116
- else
117
- Open.write(path, value.to_s)
118
- end
119
- end.any?
120
- end
121
-
122
- def self.save_job_inputs(job, dir, options = nil)
123
- options = IndiferentHash.setup options.dup if options
124
-
125
- task_name = Symbol === job.overriden ? job.overriden : job.task_name
126
- workflow = job.workflow
127
- workflow = Kernel.const_get workflow if String === workflow
128
- if workflow
129
- task_info = IndiferentHash.setup(workflow.task_info(task_name))
130
- input_types = IndiferentHash.setup(task_info[:input_types])
131
- task_inputs = IndiferentHash.setup(task_info[:inputs])
132
- input_defaults = IndiferentHash.setup(task_info[:input_defaults])
133
- else
134
- task_info = IndiferentHash.setup({})
135
- input_types = IndiferentHash.setup({})
136
- task_inputs = IndiferentHash.setup({})
137
- input_defaults = IndiferentHash.setup({})
138
- end
139
-
140
- inputs = IndiferentHash.setup({})
141
- real_inputs = job.real_inputs || job.info[:real_inputs]
142
- job.recursive_inputs.zip(job.recursive_inputs.fields).each do |value,name|
143
- next unless task_inputs.include? name.to_sym
144
- next unless real_inputs.include? name.to_sym
145
- next if options && ! options.include?(name)
146
- next if value.nil?
147
- next if input_defaults[name] == value
148
- inputs[name] = value
149
- end
150
-
151
- if options && options.include?('override_dependencies')
152
- inputs.merge!(:override_dependencies => open[:override_dependencies])
153
- input_types = IndiferentHash.setup(input_types.merge(:override_dependencies => :array))
154
- end
155
- save_inputs(inputs, input_types, dir)
156
-
157
- inputs.keys
158
- end
159
89
 
160
90
  def name
161
91
  @name ||= path.sub(/.*\/#{Regexp.quote task_name.to_s}\/(.*)/, '\1')
@@ -254,7 +254,12 @@ class Step
254
254
  when :bootstrap
255
255
  cpus = rest.nil? ? nil : rest.first
256
256
 
257
- cpus = config('dep_cpus', 'bootstrap', :default => [5, list.length / 2].min) if cpus.nil? || cpus.to_i == 0
257
+ if cpus.nil?
258
+ keys = ['bootstrap'] + list.collect{|d| [d.task_name, d.task_signature] }.flatten.uniq
259
+ cpus = config('dep_cpus', *keys, :default => [5, list.length / 2].min)
260
+ elsif Symbol === cpus
261
+ cpus = config('dep_cpus', cpus, :default => [5, list.length / 2].min)
262
+ end
258
263
 
259
264
  respawn = rest && rest.include?(:respawn)
260
265
  respawn = false if rest && rest.include?(:norespawn)
@@ -369,7 +374,8 @@ class Step
369
374
  next unless step.dependencies and step.dependencies.any?
370
375
  (step.dependencies + step.input_dependencies).each do |step_dep|
371
376
  next unless step.dependencies.include?(step_dep)
372
- next if step_dep.done? or step_dep.running? or (ComputeDependency === step_dep and (step_dep.compute == :nodup or step_dep.compute == :ignore))
377
+ next if step_dep.done? or step_dep.running? or
378
+ (ComputeDependency === step_dep and (step_dep.compute == :nodup or step_dep.compute == :ignore))
373
379
  dep_step[step_dep.path] ||= []
374
380
  dep_step[step_dep.path] << step
375
381
  end
@@ -0,0 +1,162 @@
1
+ module Workflow
2
+ def self.load_inputs(dir, input_names, input_types)
3
+ inputs = {}
4
+ if File.exists?(dir) && ! File.directory?(dir)
5
+ Log.debug "Loading inputs from #{dir}, not a directory trying as tar.gz"
6
+ tarfile = dir
7
+ digest = CMD.cmd("md5sum '#{tarfile}'").read.split(" ").first
8
+ tmpdir = Rbbt.tmp.input_bundle[digest].find
9
+ Misc.untar(tarfile, tmpdir) unless File.exists? tmpdir
10
+ files = tmpdir.glob("*")
11
+ if files.length == 1 && File.directory?(files.first)
12
+ tmpdir = files.first
13
+ end
14
+ load_inputs(tmpdir, input_names, input_types)
15
+ else
16
+ dir = Path.setup(dir.dup)
17
+ input_names.each do |input|
18
+ file = dir[input].find
19
+ file = dir.glob(input.to_s + ".*").reject{|f| f =~ /\.md5$/}.first if file.nil? or not (File.symlink?(file) || file.exists?)
20
+ Log.debug "Trying #{ input }: #{file}"
21
+ next unless file and (File.symlink?(file) || file.exists?)
22
+
23
+ type = input_types[input]
24
+
25
+ type = :io if file.split(".").last == 'as_io'
26
+
27
+ case type
28
+ when :io
29
+ inputs[input.to_sym] = Open.open(Open.realpath(file))
30
+ when :file, :binary
31
+ Log.debug "Pointing #{ input } to #{file}"
32
+ if file =~ /\.yaml/
33
+ inputs[input.to_sym] = YAML.load(Open.read(file))
34
+ else
35
+ if File.symlink?(file)
36
+ link_target = File.expand_path(File.readlink(file), File.dirname(file))
37
+ inputs[input.to_sym] = link_target
38
+ else
39
+ inputs[input.to_sym] = Open.realpath(file)
40
+ end
41
+ end
42
+ when :text
43
+ Log.debug "Reading #{ input } from #{file}"
44
+ inputs[input.to_sym] = Open.read(file)
45
+ when :array
46
+ Log.debug "Reading array #{ input } from #{file}"
47
+ inputs[input.to_sym] = Open.read(file).split("\n")
48
+ when :tsv
49
+ Log.debug "Opening tsv #{ input } from #{file}"
50
+ inputs[input.to_sym] = TSV.open(file)
51
+ when :boolean
52
+ inputs[input.to_sym] = (file.read.strip == 'true')
53
+ else
54
+ Log.debug "Loading #{ input } from #{file}"
55
+ inputs[input.to_sym] = file.read.strip
56
+ end
57
+
58
+ end
59
+ inputs = IndiferentHash.setup(inputs)
60
+
61
+ dir.glob("*#*").each do |od|
62
+ name = File.basename(od)
63
+ value = Open.read(od)
64
+ Log.debug "Loading override dependency #{ name } as #{value}"
65
+ inputs[name] = value.chomp
66
+ end
67
+
68
+ inputs
69
+ end
70
+ end
71
+
72
+ def task_inputs_from_directory(task_name, directory)
73
+ task_info = self.task_info(task_name)
74
+ Workflow.load_inputs(directory, task_info[:inputs], task_info[:input_types])
75
+ end
76
+
77
+ def job_for_directory_inputs(task_name, directory, jobname = nil)
78
+ inputs = task_inputs_from_directory(task_name, directory)
79
+ job(task_name, jobname, inputs)
80
+ end
81
+
82
+ end
83
+
84
+ class Step
85
+ def self.save_inputs(inputs, input_types, input_options, dir)
86
+ inputs.each do |name,value|
87
+ type = input_types[name]
88
+ type = type.to_s if type
89
+ path = File.join(dir, name.to_s)
90
+
91
+ path = path + '.as_io' if (IO === value || Step === value) && ! (input_options[name] && input_options[name][:nofile])
92
+ Log.debug "Saving job input #{name} (#{type}) into #{path}"
93
+
94
+ case
95
+ when IO === value
96
+ Open.write(path, value.to_s)
97
+ when Step === value
98
+ Open.ln_s(value.path, path)
99
+ when type.to_s == "file"
100
+ if String === value && File.exists?(value)
101
+ value = File.expand_path(value)
102
+ Open.ln_s(value, path)
103
+ else
104
+ value = value.collect{|v| v = "#{v}" if Path === v; v }if Array === value
105
+ value = "#{value}" if Path === value
106
+ Open.write(path + '.yaml', value.to_yaml)
107
+ end
108
+ when Array === value
109
+ Open.write(path, value.collect{|v| Step === v ? v.path : v.to_s} * "\n")
110
+ when IO === value
111
+ if value.filename && String === value.filename && File.exists?(value.filename)
112
+ Open.ln_s(value.filename, path)
113
+ else
114
+ Open.write(path, value)
115
+ end
116
+ else
117
+ Open.write(path, value.to_s)
118
+ end
119
+ end.any?
120
+ end
121
+
122
+ def self.save_job_inputs(job, dir, options = nil)
123
+ options = IndiferentHash.setup options.dup if options
124
+
125
+ task_name = Symbol === job.overriden ? job.overriden : job.task_name
126
+ workflow = job.workflow
127
+ workflow = Kernel.const_get workflow if String === workflow
128
+ if workflow
129
+ task_info = IndiferentHash.setup(workflow.task_info(task_name))
130
+ input_types = IndiferentHash.setup(task_info[:input_types])
131
+ input_options = IndiferentHash.setup(task_info[:input_options])
132
+ task_inputs = IndiferentHash.setup(task_info[:inputs])
133
+ input_defaults = IndiferentHash.setup(task_info[:input_defaults])
134
+ else
135
+ task_info = IndiferentHash.setup({})
136
+ input_types = IndiferentHash.setup({})
137
+ task_inputs = IndiferentHash.setup({})
138
+ task_options = IndiferentHash.setup({})
139
+ input_defaults = IndiferentHash.setup({})
140
+ end
141
+
142
+ inputs = IndiferentHash.setup({})
143
+ real_inputs = job.real_inputs || job.info[:real_inputs]
144
+ job.recursive_inputs.zip(job.recursive_inputs.fields).each do |value,name|
145
+ next unless task_inputs.include? name.to_sym
146
+ next unless real_inputs.include? name.to_sym
147
+ next if options && ! options.include?(name)
148
+ next if value.nil?
149
+ next if input_defaults[name] == value
150
+ inputs[name] = value
151
+ end
152
+
153
+ if options && options.include?('override_dependencies')
154
+ inputs.merge!(:override_dependencies => open[:override_dependencies])
155
+ input_types = IndiferentHash.setup(input_types.merge(:override_dependencies => :array))
156
+ end
157
+
158
+ save_inputs(inputs, input_types, input_options, dir)
159
+
160
+ inputs.keys
161
+ end
162
+ end
@@ -6,6 +6,7 @@ require 'rbbt/workflow/step/accessor'
6
6
  require 'rbbt/workflow/step/prepare'
7
7
  require 'rbbt/workflow/step/status'
8
8
  require 'rbbt/workflow/step/info'
9
+ require 'rbbt/workflow/step/save_load_inputs'
9
10
 
10
11
  class Step
11
12
  attr_accessor :clean_name, :path, :task, :workflow, :inputs, :dependencies, :bindings
@@ -321,7 +322,7 @@ class Step
321
322
  def load
322
323
  res = begin
323
324
  @result = nil if IO === @result && @result.closed?
324
- if @result && @path != @result
325
+ if @result && @path != @result && ! StreamArray === @result
325
326
  res = @result
326
327
  else
327
328
  join if not done?
@@ -90,11 +90,11 @@ module Task
90
90
 
91
91
  maps = (Array === dep and Hash === dep.last) ? dep.last.keys : []
92
92
  raise "Dependency task not found: #{dep}" if task.nil?
93
- next if seen.include? [wf, task.name]
93
+ next if seen.include? [wf, task.name, maps]
94
94
 
95
95
  task.workflow = wf if wf
96
96
 
97
- seen << [wf, task.name]
97
+ seen << [wf, task.name, maps]
98
98
  new_inputs = task.inputs - maps
99
99
  next unless new_inputs.any?
100
100
  if task_inputs[task].nil?
@@ -22,7 +22,7 @@ Use - to read from STDIN
22
22
  -h--help Print this help
23
23
  -s--sheet* Sheet to extract
24
24
  -skip--skip_rows* Initial rows to skip
25
-
25
+ -o--original Dump the rows without parsing them into TSV
26
26
  EOF
27
27
  if options[:help]
28
28
  if defined? rbbt_usage
@@ -39,5 +39,5 @@ raise ParameterException, "No excel file given" if excelfile.nil?
39
39
 
40
40
  options[:zipped] ||= true if options[:merge]
41
41
  require 'rbbt/tsv/excel'
42
- puts TSV.excel(excelfile, options).to_s
42
+ puts TSV.excel(excelfile, options.merge(:text => options[:original]))
43
43
 
@@ -104,7 +104,7 @@ def fix_options(workflow, task, job_options)
104
104
  elsif input_options[name] and input_options[name][:stream] and value == "-"
105
105
  STDIN
106
106
  else
107
- if Array === value
107
+ if Array === value || IO === value
108
108
  value
109
109
  else
110
110
  array_separator = $array_separator
@@ -137,6 +137,8 @@ def fix_options(workflow, task, job_options)
137
137
  TSV.open(STDIN, :unnamed => true, :sep => $field_separator, :sep2 => ($array_separator || "|"))
138
138
  when (Misc.is_filename?(value) and String)
139
139
  TSV.open(value, :unnamed => true, :sep => $field_separator, :sep2 => ($array_separator || "|"))
140
+ when IO
141
+ TSV.open(value, :unnamed => true, :sep => $field_separator, :sep2 => ($array_separator || "|"))
140
142
  else
141
143
  TSV.open(StringIO.new(value), :unnamed => true, :sep => $field_separator, :sep2 => ($array_separator || "|"))
142
144
  end
@@ -203,20 +203,21 @@ class TestWorkflowDependency < Test::Unit::TestCase
203
203
  size = 100000
204
204
  content = (1..size).to_a.collect{|num| "Line #{num}" } * "\n"
205
205
  last_line = nil
206
- Log.severity = 0
207
- TmpFile.with_file(content) do |input_file|
208
- begin
209
- job = DepWorkflow.job(:s3, "TEST", :input_file => input_file)
210
- job.recursive_clean
211
- job.run(:stream)
212
- io = TSV.get_stream job
213
- while line = io.gets
214
- last_line = line.strip
206
+ Log.with_severity 0 do
207
+ TmpFile.with_file(content) do |input_file|
208
+ begin
209
+ job = DepWorkflow.job(:s3, "TEST", :input_file => input_file)
210
+ job.recursive_clean
211
+ job.run(:stream)
212
+ io = TSV.get_stream job
213
+ while line = io.gets
214
+ last_line = line.strip
215
+ end
216
+ io.join if io.respond_to? :join
217
+ rescue Exception
218
+ job.abort
219
+ raise $!
215
220
  end
216
- io.join if io.respond_to? :join
217
- rescue Exception
218
- job.abort
219
- raise $!
220
221
  end
221
222
  end
222
223
  assert last_line.include? "Line #{size}"
@@ -0,0 +1,46 @@
1
+ require File.join(File.expand_path(File.dirname(__FILE__)), '../../..', 'test_helper.rb')
2
+ require 'rbbt/workflow/step/save_load_inputs'
3
+
4
+ ENV["RBBT_DEBUG_JOB_HASH"] = true.to_s
5
+ require 'rbbt/workflow'
6
+ module TestSaveLoadWF
7
+ extend Workflow
8
+
9
+ task :number => :integer do
10
+ 10
11
+ end
12
+
13
+ task :list => :array do
14
+ (0..10).to_a.collect{|e| e.to_s}
15
+ end
16
+
17
+ input :list, :array
18
+ input :number, :integer
19
+ task :reverse => :array do |list|
20
+ list.reverse
21
+ end
22
+
23
+ dep :list
24
+ dep :number
25
+ dep :reverse, :list => :list, :number => :number
26
+ task :prefix => :array do
27
+ step(:reverse).run.collect{|e| "A-#{e}" }
28
+ end
29
+ end
30
+
31
+ class TestSaveLoad < Test::Unit::TestCase
32
+ def test_save
33
+ Log.with_severity 0 do
34
+ job = TestSaveLoadWF.job(:prefix)
35
+ job.recursive_clean
36
+ job = TestSaveLoadWF.job(:prefix)
37
+ TmpFile.with_file do |directory|
38
+ Step.save_job_inputs(job.step(:reverse), directory)
39
+ job.produce
40
+ newjob = TestSaveLoadWF.job_for_directory_inputs(:reverse, directory)
41
+ assert_equal job.rec_dependencies.last.path, newjob.path
42
+ end
43
+ end
44
+ end
45
+ end
46
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rbbt-util
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.32.26
4
+ version: 5.32.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Miguel Vazquez
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-10 00:00:00.000000000 Z
11
+ date: 2021-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -345,6 +345,7 @@ files:
345
345
  - lib/rbbt/workflow/step/info.rb
346
346
  - lib/rbbt/workflow/step/prepare.rb
347
347
  - lib/rbbt/workflow/step/run.rb
348
+ - lib/rbbt/workflow/step/save_load_inputs.rb
348
349
  - lib/rbbt/workflow/step/status.rb
349
350
  - lib/rbbt/workflow/task.rb
350
351
  - lib/rbbt/workflow/usage.rb
@@ -550,6 +551,7 @@ files:
550
551
  - test/rbbt/util/test_simpleopt.rb
551
552
  - test/rbbt/util/test_tmpfile.rb
552
553
  - test/rbbt/workflow/step/test_dependencies.rb
554
+ - test/rbbt/workflow/step/test_save_load_inputs.rb
553
555
  - test/rbbt/workflow/test_doc.rb
554
556
  - test/rbbt/workflow/test_remote_workflow.rb
555
557
  - test/rbbt/workflow/test_schedule.rb
@@ -592,6 +594,7 @@ test_files:
592
594
  - test/rbbt/workflow/test_schedule.rb
593
595
  - test/rbbt/workflow/test_step.rb
594
596
  - test/rbbt/workflow/step/test_dependencies.rb
597
+ - test/rbbt/workflow/step/test_save_load_inputs.rb
595
598
  - test/rbbt/workflow/test_task.rb
596
599
  - test/rbbt/resource/test_path.rb
597
600
  - test/rbbt/util/test_colorize.rb