-
Notifications
You must be signed in to change notification settings - Fork 60
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added tests for JobManager #263
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,142 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
import unittest | ||||||||||||||||||||||||||||||||||||||||||||||||||
import redis | ||||||||||||||||||||||||||||||||||||||||||||||||||
from mock import MagicMock, patch | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
from jobManager import JobManager | ||||||||||||||||||||||||||||||||||||||||||||||||||
from jobQueue import JobQueue | ||||||||||||||||||||||||||||||||||||||||||||||||||
from tangoObjects import TangoJob, TangoMachine | ||||||||||||||||||||||||||||||||||||||||||||||||||
from config import Config | ||||||||||||||||||||||||||||||||||||||||||||||||||
from preallocator import * | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
class TestJobManager(unittest.TestCase): | ||||||||||||||||||||||||||||||||||||||||||||||||||
def createTangoMachine(self, image, vmms, vmObj={"cores": 1, "memory": 512}): | ||||||||||||||||||||||||||||||||||||||||||||||||||
"""createTangoMachine - Creates a tango machine object from image""" | ||||||||||||||||||||||||||||||||||||||||||||||||||
return TangoMachine( | ||||||||||||||||||||||||||||||||||||||||||||||||||
name=image, | ||||||||||||||||||||||||||||||||||||||||||||||||||
vmms=vmms, | ||||||||||||||||||||||||||||||||||||||||||||||||||
image="%s" % (image), | ||||||||||||||||||||||||||||||||||||||||||||||||||
cores=vmObj["cores"], | ||||||||||||||||||||||||||||||||||||||||||||||||||
memory=vmObj["memory"], | ||||||||||||||||||||||||||||||||||||||||||||||||||
disk=None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
network=None, | ||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+13
to
+24
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Fix mutable default argument in createTangoMachine method. Using mutable default arguments in Python can lead to unexpected behavior if the default value is modified. Apply this diff: - def createTangoMachine(self, image, vmms, vmObj={"cores": 1, "memory": 512}):
+ def createTangoMachine(self, image, vmms, vmObj=None):
"""createTangoMachine - Creates a tango machine object from image"""
+ if vmObj is None:
+ vmObj = {"cores": 1, "memory": 512}
return TangoMachine(
name=image,
vmms=vmms,
image="%s" % (image),
cores=vmObj["cores"],
memory=vmObj["memory"],
disk=None,
network=None,
) 📝 Committable suggestion
Suggested change
🧰 Tools🪛 Ruff13-13: Do not use mutable data structures for argument defaults Replace with (B006) |
||||||||||||||||||||||||||||||||||||||||||||||||||
def setUp(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
if Config.USE_REDIS: | ||||||||||||||||||||||||||||||||||||||||||||||||||
__db = redis.StrictRedis(Config.REDIS_HOSTNAME, Config.REDIS_PORT, db=0) | ||||||||||||||||||||||||||||||||||||||||||||||||||
__db.flushall() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
if Config.VMMS_NAME == "ec2SSH": | ||||||||||||||||||||||||||||||||||||||||||||||||||
from vmms.ec2SSH import Ec2SSH | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
vmms = Ec2SSH() | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.preallocator = Preallocator({"ec2SSH": vmms}) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
elif Config.VMMS_NAME == "localDocker": | ||||||||||||||||||||||||||||||||||||||||||||||||||
from vmms.localDocker import LocalDocker | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
vmms = LocalDocker() | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.preallocator = Preallocator({"localDocker": vmms}) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
elif Config.VMMS_NAME == "distDocker": | ||||||||||||||||||||||||||||||||||||||||||||||||||
from vmms.distDocker import DistDocker | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
vmms = DistDocker() | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.preallocator = Preallocator({"distDocker": vmms}) | ||||||||||||||||||||||||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||||||||||||||||||||||||
vmms = None | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.preallocator = Preallocator({"default": vmms}) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
self.job1 = TangoJob( | ||||||||||||||||||||||||||||||||||||||||||||||||||
name="sample_job_1", | ||||||||||||||||||||||||||||||||||||||||||||||||||
vm="ilter.img", | ||||||||||||||||||||||||||||||||||||||||||||||||||
outputFile="sample_job_1_output", | ||||||||||||||||||||||||||||||||||||||||||||||||||
input=[], | ||||||||||||||||||||||||||||||||||||||||||||||||||
timeout=30, | ||||||||||||||||||||||||||||||||||||||||||||||||||
notifyURL="notifyMeUrl", | ||||||||||||||||||||||||||||||||||||||||||||||||||
maxOutputFileSize=4096, | ||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
self.job2 = TangoJob( | ||||||||||||||||||||||||||||||||||||||||||||||||||
name="sample_job_2", | ||||||||||||||||||||||||||||||||||||||||||||||||||
vm="ilter.img", | ||||||||||||||||||||||||||||||||||||||||||||||||||
outputFile="sample_job_2_output", | ||||||||||||||||||||||||||||||||||||||||||||||||||
input=[], | ||||||||||||||||||||||||||||||||||||||||||||||||||
timeout=30, | ||||||||||||||||||||||||||||||||||||||||||||||||||
notifyURL="notifyMeUrl", | ||||||||||||||||||||||||||||||||||||||||||||||||||
maxOutputFileSize=4096, | ||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobQueue = JobQueue(self.preallocator) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobQueue.reset() | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobManager = JobManager(self.jobQueue) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.vm = self.createTangoMachine(image="autograding_image", vmms=vmms) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
def test_start(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobManager.start() | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.assertTrue(self.jobManager.running) | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
def test__getNextID(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||
init_id = self.jobManager.nextId | ||||||||||||||||||||||||||||||||||||||||||||||||||
for i in range(1, Config.MAX_JOBID + 100): | ||||||||||||||||||||||||||||||||||||||||||||||||||
id = self.jobManager._getNextID() | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.assertEqual(init_id + i - 1, id) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobManager.nextId = init_id | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
def test_manage_worker_works(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||
def mock_getNextPendingJob(): | ||||||||||||||||||||||||||||||||||||||||||||||||||
time.sleep(0.2) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.job1.setId(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||
return self.job1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
def mock_reuseVM(job): | ||||||||||||||||||||||||||||||||||||||||||||||||||
job.assigned = True | ||||||||||||||||||||||||||||||||||||||||||||||||||
job.vm = self.vm | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.preallocator.vmms[job.vm.vmms] = self.jobManager.vmms | ||||||||||||||||||||||||||||||||||||||||||||||||||
return self.vm | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
def mock_worker_init(job, vmms, jobQueue, preallocator, preVM): | ||||||||||||||||||||||||||||||||||||||||||||||||||
pass | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobQueue.assignJob = MagicMock() | ||||||||||||||||||||||||||||||||||||||||||||||||||
with patch( | ||||||||||||||||||||||||||||||||||||||||||||||||||
"jobQueue.JobQueue.getNextPendingJob", side_effect=mock_getNextPendingJob | ||||||||||||||||||||||||||||||||||||||||||||||||||
), patch("jobQueue.JobQueue.reuseVM", side_effect=mock_reuseVM), patch( | ||||||||||||||||||||||||||||||||||||||||||||||||||
"worker.Worker.__init__", side_effect=mock_worker_init | ||||||||||||||||||||||||||||||||||||||||||||||||||
): | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobManager.start() | ||||||||||||||||||||||||||||||||||||||||||||||||||
time.sleep(2) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobQueue.assignJob.assert_called() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+88
to
+112
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Improve reliability of worker tests. The worker tests have several potential issues:
Consider these improvements:
Example implementation: def test_manage_worker_works(self):
job_ready = threading.Event()
def mock_getNextPendingJob():
job_ready.wait(timeout=1) # More reliable than sleep
self.job1.setId(1)
return self.job1
# ... rest of the test ...
self.jobManager.start()
job_ready.set() # Signal that job is ready
self.jobQueue.assignJob.assert_called_with(self.job1)
self.assertTrue(self.job1.assigned)
self.assertEqual(self.job1.vm, self.vm) Would you like me to provide a complete implementation with these improvements? Also applies to: 113-139 🧰 Tools🪛 Ruff90-90: (F405) 110-110: (F405) |
||||||||||||||||||||||||||||||||||||||||||||||||||
def test_manage_worker_fails(self): | ||||||||||||||||||||||||||||||||||||||||||||||||||
def mock_getNextPendingJob(): | ||||||||||||||||||||||||||||||||||||||||||||||||||
time.sleep(2) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.job1.setId(1) | ||||||||||||||||||||||||||||||||||||||||||||||||||
return self.job1 | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
def mock_reuseVM(job): | ||||||||||||||||||||||||||||||||||||||||||||||||||
job.assigned = True | ||||||||||||||||||||||||||||||||||||||||||||||||||
job.vm = self.vm | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.preallocator.vmms[job.vm.vmms] = self.jobManager.vmms | ||||||||||||||||||||||||||||||||||||||||||||||||||
return self.vm | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
def mock_worker_init(job, vmms, jobQueue, preallocator, preVM): | ||||||||||||||||||||||||||||||||||||||||||||||||||
raise RuntimeError("Job failed") | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobQueue.assignJob = MagicMock() | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobQueue.makeDead = MagicMock() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
with patch( | ||||||||||||||||||||||||||||||||||||||||||||||||||
"jobQueue.JobQueue.getNextPendingJob", side_effect=mock_getNextPendingJob | ||||||||||||||||||||||||||||||||||||||||||||||||||
), patch("jobQueue.JobQueue.reuseVM", side_effect=mock_reuseVM), patch( | ||||||||||||||||||||||||||||||||||||||||||||||||||
"worker.Worker.__init__", side_effect=mock_worker_init | ||||||||||||||||||||||||||||||||||||||||||||||||||
): | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobManager.start() | ||||||||||||||||||||||||||||||||||||||||||||||||||
time.sleep(4) | ||||||||||||||||||||||||||||||||||||||||||||||||||
self.jobQueue.makeDead.assert_called_once() | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
if __name__ == "__main__": | ||||||||||||||||||||||||||||||||||||||||||||||||||
unittest.main() | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+1
to
+142
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add missing test cases for comprehensive coverage. The current test suite is missing several important test cases:
Would you like me to help generate additional test cases to improve coverage? 🧰 Tools🪛 Ruff9-9: (F403) 13-13: Do not use mutable data structures for argument defaults Replace with (B006) 35-35: (F405) 41-41: (F405) 47-47: (F405) 50-50: (F405) 90-90: (F405) 110-110: (F405) 115-115: (F405) 137-137: (F405) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fix import statements for better maintainability and reliability.
Apply this diff:
📝 Committable suggestion
🧰 Tools
🪛 Ruff
9-9:
from preallocator import *
used; unable to detect undefined names(F403)