From 9c823acbff6c94046879eabf4f10ce886b018be0 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 15 Oct 2026 15:48:58 -0400 Subject: [PATCH 1/5] add test enforcing adapter interface --- test/job/adapter_test.rb | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 test/job/adapter_test.rb diff --git a/test/job/adapter_test.rb b/test/job/adapter_test.rb new file mode 100644 index 000000000..2baed8f3e --- /dev/null +++ b/test/job/adapter_test.rb @@ -0,0 +1,42 @@ +require 'test_helper' +require 'ood_core/job/adapter.rb' + +ADAPTER_FILES = Dir[File.join('ood_core', 'job', 'adapters', '*.rb')] +ADAPTER_FILES.each { |file| require file } + +class AdapterTest < Minitest::Test + BASE = OodCore::Job::Adapter + + # Every concrete class descending from the base adapter + def adapters + ObjectSpace.each_object(Class).select { |klass| klass < BASE } + end + + # Public methods defined by the base class itself (not inherited from Object) + def base_methods + BASE.public_instance_methods(false) + end + + def test_adapters_were_found + refute_empty adapters, 'No subclasses of OodCore::Job::Adapter were found' + end + + def test_adapter_interfaces_match_base_class + adapters.each do |adapter| + base_methods.each do |name| + impl = adapter.instance_method(name) + + # Case 2: not overridden, so the base class method runs + next if impl.owner == BASE + + # Case 1: overridden, so the signature must match the base exactly + expected = BASE.instance_method(name).parameters + actual = impl.parameters + + assert_equal expected, actual, + "#{adapter}##{name} (defined in #{impl.owner}) has parameters " \ + "#{actual.inspect}, but #{BASE}##{name} defines #{expected.inspect}" + end + end + end +end \ No newline at end of file From a0ca023f53de3c58b17f69f7cd0c6f90bf60b7f4 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 15 Oct 2026 15:59:45 -0400 Subject: [PATCH 2/5] Fix comments --- test/job/adapter_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/job/adapter_test.rb b/test/job/adapter_test.rb index 2baed8f3e..256da30ee 100644 --- a/test/job/adapter_test.rb +++ b/test/job/adapter_test.rb @@ -26,10 +26,10 @@ def test_adapter_interfaces_match_base_class base_methods.each do |name| impl = adapter.instance_method(name) - # Case 2: not overridden, so the base class method runs + # If the method is not overridden, the base class method runs next if impl.owner == BASE - # Case 1: overridden, so the signature must match the base exactly + # If it is overridden, the signature must match the base exactly expected = BASE.instance_method(name).parameters actual = impl.parameters From 5784cad150c9dabd6c99fa3733d262e74ea052c4 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 15 Oct 2026 16:09:34 -0400 Subject: [PATCH 3/5] Fix obvious interface gaps --- lib/ood_core/job/adapters/ccq.rb | 18 ------------------ lib/ood_core/job/adapters/htcondor.rb | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/lib/ood_core/job/adapters/ccq.rb b/lib/ood_core/job/adapters/ccq.rb index 042bfe873..81a842ced 100644 --- a/lib/ood_core/job/adapters/ccq.rb +++ b/lib/ood_core/job/adapters/ccq.rb @@ -104,24 +104,6 @@ def status(id) info(id).status end - # This adapter does not implement hold and will always raise - # an exception. - # @param id [#to_s] the id of the job - # @raise [JobAdapterError] always - # @return [void] - def hold(_) - raise NotImplementedError, "subclass did not define #hold" - end - - # This adapter does not implement release and will always raise - # an exception. - # @param id [#to_s] the id of the job - # @raise [JobAdapterError] always - # @return [void] - def release(_) - raise NotImplementedError, "subclass did not define #release" - end - # Delete the submitted job # @param id [#to_s] the id of the job # @return [void] diff --git a/lib/ood_core/job/adapters/htcondor.rb b/lib/ood_core/job/adapters/htcondor.rb index 553af10ea..1301ecf20 100644 --- a/lib/ood_core/job/adapters/htcondor.rb +++ b/lib/ood_core/job/adapters/htcondor.rb @@ -314,7 +314,7 @@ def initialize(opts = {}) # @raise [JobAdapterError] if something goes wrong submitting a job # @return [String] the job id returned after successfully submitting a # job - def submit(script) + def submit(script, after: [], afterok: [], afternotok: [], afterany: []) validate_native(script, Hash) args = [] args.concat ["-batch-name", "#{script.job_name}"] unless script.job_name.nil? From 49de5427e184a8af377eabc68745d5d20bff4b5d Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 15 Oct 2026 16:38:42 -0400 Subject: [PATCH 4/5] Add `host:` kwarg to all adapters' `#info_all` --- lib/ood_core/job/adapter.rb | 9 ++++++++- lib/ood_core/job/adapters/ccq.rb | 2 +- lib/ood_core/job/adapters/coder.rb | 2 +- lib/ood_core/job/adapters/fujitsu_tcs.rb | 2 +- lib/ood_core/job/adapters/htcondor.rb | 2 +- lib/ood_core/job/adapters/kubernetes.rb | 2 +- lib/ood_core/job/adapters/lsf.rb | 2 +- lib/ood_core/job/adapters/pbspro.rb | 2 +- lib/ood_core/job/adapters/psij.rb | 2 +- lib/ood_core/job/adapters/sge.rb | 2 +- lib/ood_core/job/adapters/slurm.rb | 2 +- lib/ood_core/job/adapters/torque.rb | 2 +- 12 files changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/ood_core/job/adapter.rb b/lib/ood_core/job/adapter.rb index f91af96e5..fa2bd0e2f 100644 --- a/lib/ood_core/job/adapter.rb +++ b/lib/ood_core/job/adapter.rb @@ -52,8 +52,15 @@ def cluster_info # # For certain adapters this may speed up the response since # adapters can get by without populating the entire Info object + # + # @param host [#to_s, nil] defaults to nil (and all hosts are queried) + # Restricts the listing to the specified host. Only meaningful for adapters + # that manage jobs across multiple hosts, like LinuxHost or Systemd. Adapters + # that talk to a single scheduler will accept and ignore it. Adapters + # that honor it will raise unless the specified host is included in + # the ssh_hosts adapter configuration. # @return [Array] information describing submitted jobs - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) raise NotImplementedError, "subclass did not define #info_all" end diff --git a/lib/ood_core/job/adapters/ccq.rb b/lib/ood_core/job/adapters/ccq.rb index 81a842ced..b7cf6d445 100644 --- a/lib/ood_core/job/adapters/ccq.rb +++ b/lib/ood_core/job/adapters/ccq.rb @@ -72,7 +72,7 @@ def submit(script, after: [], afterok: [], afternotok: [], afterany: []) # Retrieve info for all jobs from the resource manager # @return [Array] information describing submitted jobs - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) args = [] args.concat ["-s", scheduler] unless scheduler.nil? diff --git a/lib/ood_core/job/adapters/coder.rb b/lib/ood_core/job/adapters/coder.rb index 019cf4949..91ff1a78e 100644 --- a/lib/ood_core/job/adapters/coder.rb +++ b/lib/ood_core/job/adapters/coder.rb @@ -89,7 +89,7 @@ def submit(script, after: [], afterok: [], afternotok: [], afterany: []) # For certain adapters this may speed up the response since # adapters can get by without populating the entire Info object # @return [Array] information describing submitted jobs - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) [] end diff --git a/lib/ood_core/job/adapters/fujitsu_tcs.rb b/lib/ood_core/job/adapters/fujitsu_tcs.rb index 3d1704f8b..2be5522db 100644 --- a/lib/ood_core/job/adapters/fujitsu_tcs.rb +++ b/lib/ood_core/job/adapters/fujitsu_tcs.rb @@ -273,7 +273,7 @@ def submit(script, after: [], afterok: [], afternotok: [], afterany: []) # @raise [JobAdapterError] if something goes wrong getting job info # @return [Array] information describing submitted jobs # @see Adapter#info_all - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) @fujitsu_tcs.get_jobs().map do |v| parse_job_info(v) end diff --git a/lib/ood_core/job/adapters/htcondor.rb b/lib/ood_core/job/adapters/htcondor.rb index 1301ecf20..9f31bc05c 100644 --- a/lib/ood_core/job/adapters/htcondor.rb +++ b/lib/ood_core/job/adapters/htcondor.rb @@ -432,7 +432,7 @@ def info(id) # Retrieve information for all jobs # @raise [JobAdapterError] if something goes wrong retrieving job info # @return [Array] list of information describing submitted jobs - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) jobs = @htcondor.get_jobs jobs.map { |job| parse_job_info(job) } rescue Batch::Error => e diff --git a/lib/ood_core/job/adapters/kubernetes.rb b/lib/ood_core/job/adapters/kubernetes.rb index 77d93130f..f67b1a45a 100644 --- a/lib/ood_core/job/adapters/kubernetes.rb +++ b/lib/ood_core/job/adapters/kubernetes.rb @@ -72,7 +72,7 @@ def submit(script, after: [], afterok: [], afternotok: [], afterany: []) # For certain adapters this may speed up the response since # adapters can get by without populating the entire Info object # @return [Array] information describing submitted jobs - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) batch.info_all(attrs: attrs) rescue Batch::Error => e raise JobAdapterError, e.message diff --git a/lib/ood_core/job/adapters/lsf.rb b/lib/ood_core/job/adapters/lsf.rb index b06b8303d..f556221ae 100644 --- a/lib/ood_core/job/adapters/lsf.rb +++ b/lib/ood_core/job/adapters/lsf.rb @@ -104,7 +104,7 @@ def info(id) # @raise [JobAdapterError] if something goes wrong getting job info # @return [Array] information describing submitted jobs # @see Adapter#info_all - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) batch.get_jobs.map { |v| info_for_batch_hash(v) } rescue Batch::Error => e raise JobAdapterError, e.message diff --git a/lib/ood_core/job/adapters/pbspro.rb b/lib/ood_core/job/adapters/pbspro.rb index da8146f5c..72ea438ac 100644 --- a/lib/ood_core/job/adapters/pbspro.rb +++ b/lib/ood_core/job/adapters/pbspro.rb @@ -360,7 +360,7 @@ def cluster_info # @raise [JobAdapterError] if something goes wrong getting job info # @return [Array] information describing submitted jobs # @see Adapter#info_all - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) @pbspro.get_jobs.map do |v| parse_job_info(v) end diff --git a/lib/ood_core/job/adapters/psij.rb b/lib/ood_core/job/adapters/psij.rb index 988f7ab9c..fefc6601b 100755 --- a/lib/ood_core/job/adapters/psij.rb +++ b/lib/ood_core/job/adapters/psij.rb @@ -345,7 +345,7 @@ def info(id) end end - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) @psij.get_jobs.map do |v| parse_job_info(v) end diff --git a/lib/ood_core/job/adapters/sge.rb b/lib/ood_core/job/adapters/sge.rb index 095062db3..aca977a67 100644 --- a/lib/ood_core/job/adapters/sge.rb +++ b/lib/ood_core/job/adapters/sge.rb @@ -93,7 +93,7 @@ def submit(script, after: [], afterok: [], afternotok: [], afterany: []) # Retrieve info for all jobs from the resource manager # @return [Array] information describing submitted jobs - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) @batch.get_all(owner: '*') rescue Batch::Error => e raise JobAdapterError, e.message diff --git a/lib/ood_core/job/adapters/slurm.rb b/lib/ood_core/job/adapters/slurm.rb index c96655676..d861aefc4 100644 --- a/lib/ood_core/job/adapters/slurm.rb +++ b/lib/ood_core/job/adapters/slurm.rb @@ -755,7 +755,7 @@ def accounts # @raise [JobAdapterError] if something goes wrong getting job info # @return [Array] information describing submitted jobs # @see Adapter#info_all - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) @slurm.get_jobs(attrs: attrs).map do |v| parse_job_info(v) end diff --git a/lib/ood_core/job/adapters/torque.rb b/lib/ood_core/job/adapters/torque.rb index b71021d20..dc6b10bca 100644 --- a/lib/ood_core/job/adapters/torque.rb +++ b/lib/ood_core/job/adapters/torque.rb @@ -187,7 +187,7 @@ def submit(script, after: [], afterok: [], afternotok: [], afterany: []) # @raise [JobAdapterError] if something goes wrong getting job info # @return [Array] information describing submitted jobs # @see Adapter#info_all - def info_all(attrs: nil) + def info_all(attrs: nil, host: nil) @pbs.get_jobs.map do |k, v| parse_job_info(k, v) end From 645025cce432367093e6c4c5024cc8c29df2de79 Mon Sep 17 00:00:00 2001 From: Braeden Singleton Date: Thu, 15 Oct 2026 16:41:29 -0400 Subject: [PATCH 5/5] name blank args in info_where_owner --- lib/ood_core/job/adapters/linux_host.rb | 2 +- lib/ood_core/job/adapters/systemd.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/ood_core/job/adapters/linux_host.rb b/lib/ood_core/job/adapters/linux_host.rb index 2a2693b94..730c8a746 100644 --- a/lib/ood_core/job/adapters/linux_host.rb +++ b/lib/ood_core/job/adapters/linux_host.rb @@ -107,7 +107,7 @@ def info_all(attrs: nil, host: nil) # @param owner [#to_s, Array<#to_s>] the owner(s) of the jobs # @raise [JobAdapterError] if something goes wrong getting job info # @return [Array] information describing submitted jobs - def info_where_owner(_, attrs: nil) + def info_where_owner(owner, attrs: nil) info_all end diff --git a/lib/ood_core/job/adapters/systemd.rb b/lib/ood_core/job/adapters/systemd.rb index 655c0c876..f4cfd28f4 100644 --- a/lib/ood_core/job/adapters/systemd.rb +++ b/lib/ood_core/job/adapters/systemd.rb @@ -95,7 +95,7 @@ def info_all(attrs: nil, host: nil) # @param owner [#to_s, Array<#to_s>] the owner(s) of the jobs # @raise [JobAdapterError] if something goes wrong getting job info # @return [Array] information describing submitted jobs - def info_where_owner(_, attrs: nil) + def info_where_owner(owner, attrs: nil) info_all end