13 Unit tests for the backends.py classes and functions. This is a little difficult to test due to
14 the Batch system backends not really being testable when not running on specific hosts with bsub/qsub
15 installed. But we can try to implement tests using the Local multiprocessing backend and testing the
16 basic behaviour of the classes so that they will fail if we modify assumptions in the future.
19 from basf2
import find_file
22 from unittest
import TestCase
24 from pathlib
import Path
26 from caf.backends
import ArgumentsSplitter, Job, MaxFilesSplitter, MaxSubjobsSplitter
27 from caf.backends
import ArgumentsGenerator, range_arguments, SplitterError
30 test_dir = Path(
"test_backends").absolute()
32 test_script = Path(find_file(
"calibration/examples/job_submission/test_script.sh")).absolute()
37 UnitTest for the `caf.backends.Job` class
42 Create useful objects for each test and the teardown
48 job1.working_dir = Path(test_dir, job1.name,
"working_dir").absolute().as_posix()
49 job1.output_dir = Path(test_dir, job1.name,
"output_dir").absolute().as_posix()
50 job1.cmd = [
"bash", test_script.name]
51 job1.input_sandbox_files = [test_script.as_posix()]
56 job_dict[
"name"] = name2
57 job_dict[
"working_dir"] = Path(test_dir, name2,
"working_dir").as_posix()
58 job_dict[
"output_dir"] = Path(test_dir, name2,
"output_dir").as_posix()
59 job_dict[
"output_patterns"] = []
60 job_dict[
"cmd"] = [
"bash", test_script.name]
62 job_dict[
"input_sandbox_files"] = [test_script.as_posix()]
63 job_dict[
"input_files"] = []
64 job_dict[
"setup_cmds"] = []
65 job_dict[
"backend_args"] = {}
66 job_dict[
"subjobs"] = [{
"id": i,
"input_files": [],
"args": [str(i)]}
for i
in range(4)]
68 self.
job2job2 = Job(name2, job_dict=job_dict)
71 test_dir.mkdir(parents=
True, exist_ok=
False)
73 def test_dict_setup(self):
75 self.assertEqual(len(self.
job2job2.subjobs), 4)
76 self.assertEqual(self.
job2_dictjob2_dict, self.
job2job2.job_dict)
78 del self.
job2job2.subjobs[3]
79 self.assertEqual(self.
job2_dictjob2_dict, self.
job2job2.job_dict)
81 def test_job_json_serialise(self):
82 json_path = Path(test_dir,
"job2.json")
83 self.
job2job2.dump_to_json(json_path)
84 job2_copy = Job.from_json(json_path)
85 self.assertEqual(self.
job2job2.job_dict, job2_copy.job_dict)
89 The Jobs haven't been run so they should be in the 'init' status.
90 They also shouldn't throw exceptions due to missing result objects.
92 self.assertEqual(self.
job1job1.status,
"init")
93 self.assertEqual(self.
job2job2.status,
"init")
94 self.assertFalse(self.
job1job1.ready())
95 self.assertFalse(self.
job2job2.ready())
96 self.assertEqual(self.
job1job1.update_status(),
"init")
97 self.assertEqual(self.
job2job2.update_status(),
"init")
98 for subjob
in self.
job2job2.subjobs.values():
99 self.assertEqual(subjob.status,
"init")
100 self.assertFalse(subjob.ready())
101 self.assertEqual(subjob.update_status(),
"init")
105 Make sure that the two ways of setting up Job objects correctly converted attributes to be Paths instead of strings.
107 self.assertIsInstance(self.
job1job1.output_dir, Path)
108 self.assertIsInstance(self.
job1job1.working_dir, Path)
109 for path
in self.
job1job1.input_sandbox_files:
110 self.assertIsInstance(path, Path)
111 for path
in self.
job1job1.input_files:
112 self.assertIsInstance(path, Path)
114 self.assertIsInstance(self.
job2job2.output_dir, Path)
115 self.assertIsInstance(self.
job2job2.working_dir, Path)
116 for path
in self.
job2job2.input_sandbox_files:
117 self.assertIsInstance(path, Path)
118 for path
in self.
job2job2.input_files:
119 self.assertIsInstance(path, Path)
121 for subjob
in self.
job2job2.subjobs.values():
122 self.assertIsInstance(subjob.output_dir, Path)
123 self.assertIsInstance(subjob.working_dir, Path)
127 Test the creation of SubJobs and assignment of input data files via splitter classes.
129 self.assertIsNone(self.
job1job1.splitter)
130 self.assertIsNone(self.
job2job2.splitter)
132 self.
job1job1.max_files_per_subjob = 2
133 self.assertIsInstance(self.
job1job1.splitter, MaxFilesSplitter)
134 self.assertEqual(self.
job1job1.splitter.max_files_per_subjob, 2)
135 self.
job1job1.max_subjobs = 3
136 self.assertIsInstance(self.
job1job1.splitter, MaxSubjobsSplitter)
137 self.assertEqual(self.
job1job1.splitter.max_subjobs, 3)
141 input_file = Path(test_dir, f
"{i}.txt")
142 input_file.touch(exist_ok=
False)
143 self.
job1job1.input_files.append(input_file)
145 self.
job1job1.splitter = MaxFilesSplitter(max_files_per_subjob=2)
146 self.
job1job1.splitter.create_subjobs(self.
job1job1)
147 self.assertEqual(len(self.
job1job1.subjobs), 3)
148 for i, subjob
in self.
job1job1.subjobs.items():
149 self.assertTrue((len(subjob.input_files) == 2
or len(subjob.input_files) == 1))
151 self.
job1job1.subjobs = {}
152 self.
job1job1.splitter = MaxSubjobsSplitter(max_subjobs=4)
153 self.
job1job1.splitter.create_subjobs(self.
job1job1)
154 self.assertEqual(len(self.
job1job1.subjobs), 4)
155 for i, subjob
in self.
job1job1.subjobs.items():
156 self.assertTrue((len(subjob.input_files) == 2
or len(subjob.input_files) == 1))
159 self.
job1job1.subjobs = {}
160 arg_gen = ArgumentsGenerator(range_arguments, 3, stop=12, step=2)
161 self.
job1job1.splitter = ArgumentsSplitter(arguments_generator=arg_gen, max_subjobs=10)
162 self.
job1job1.splitter.create_subjobs(self.
job1job1)
163 self.assertEqual(len(self.
job1job1.subjobs), 5)
164 for (i, subjob), arg
in zip(self.
job1job1.subjobs.items(), range(3, 12, 2)):
166 self.assertEqual(self.
job1job1.input_files, subjob.input_files)
167 self.assertEqual(arg, subjob.args[0])
170 self.
job1job1.subjobs = {}
171 self.
job1job1.splitter = ArgumentsSplitter(arguments_generator=arg_gen, max_subjobs=2)
172 self.assertRaises(SplitterError, self.
job1job1.splitter.create_subjobs, self.
job1job1)
176 Does the copy of files/directories for the input sandbox work correctly?
179 input_sandbox_dir = Path(test_dir,
"test_input_sandbox_dir")
180 input_sandbox_dir.mkdir(parents=
True, exist_ok=
False)
182 input_file = Path(input_sandbox_dir, f
"{i}.txt")
183 input_file.touch(exist_ok=
False)
185 self.
job1job1.input_sandbox_files.append(input_sandbox_dir)
187 self.
job1job1.working_dir.mkdir(parents=
True, exist_ok=
False)
188 self.
job1job1.copy_input_sandbox_files_to_working_dir()
192 expected_paths.append(Path(self.
job1job1.working_dir, test_script.name))
193 expected_paths.append(Path(self.
job1job1.working_dir,
"test_input_sandbox_dir"))
195 path = Path(self.
job1job1.working_dir,
"test_input_sandbox_dir", f
"{i}.txt")
196 expected_paths.append(path)
199 for p
in self.
job1job1.working_dir.rglob(
"*"):
200 self.assertIn(p, expected_paths)
204 Removes files/directories that were created during these tests
206 shutil.rmtree(test_dir)
213 if __name__ ==
'__main__':
def test_subjob_splitting(self)
def test_path_object_conversion(self)
def test_input_sandbox_copy(self)
int main(int argc, char **argv)
Run all tests.