More task processing unit testing

pull/94/head
Piero Toffanin 2017-02-07 17:09:30 -05:00
rodzic 84c0d449be
commit c5b78675a4
2 zmienionych plików z 77 dodań i 22 usunięć

Wyświetl plik

@ -189,8 +189,8 @@ class Task(models.Model):
try:
if self.processing_node:
# Need to process some images (UUID not yet set and task not marked for deletion)?
if not self.uuid and self.pending_action != pending_actions.REMOVE:
# Need to process some images (UUID not yet set and task doesn't have pending actions)?
if not self.uuid and self.pending_action is None:
logger.info("Processing... {}".format(self))
images = [image.path() for image in self.imageupload_set.all()]
@ -223,24 +223,25 @@ class Task(models.Model):
raise ProcessingException("Cannot cancel a task that has no processing node or UUID")
elif self.pending_action == pending_actions.RESTART:
logger.info("Restarting task {}".format(self))
if self.processing_node and self.uuid:
logger.info("Restarting {}".format(self))
if self.processing_node:
# Check if the UUID is still valid, as processing nodes purge
# results after a set amount of time, the UUID might have eliminated.
try:
info = self.processing_node.get_task_info(self.uuid)
uuid_still_exists = info['uuid'] == self.uuid
except ProcessingException:
uuid_still_exists = False
uuid_still_exists = False
if self.uuid:
try:
info = self.processing_node.get_task_info(self.uuid)
uuid_still_exists = info['uuid'] == self.uuid
except ProcessingException:
pass
if uuid_still_exists:
# Good to go
self.processing_node.restart_task(self.uuid)
else:
# Task has been purged (or processing node is offline)
# TODO: what if processing node went offline?
# Process this as a new task
# Removing its UUID will cause the scheduler
# to process this the next tick

Wyświetl plik

@ -9,6 +9,7 @@ from django.contrib.auth.models import User
from rest_framework import status
from rest_framework.test import APIClient
from app import pending_actions
from app import scheduler
from app.models import Project, Task, ImageUpload, task_directory_path
from app.tests.classes import BootTransactionTestCase
@ -23,6 +24,15 @@ from app.testwatch import testWatch
from webodm import settings
logger = logging.getLogger('app.logger')
DELAY = 1 # time to sleep for during process launch, background processing, etc.
def start_processing_node():
current_dir = os.path.dirname(os.path.realpath(__file__))
node_odm = subprocess.Popen(['node', 'index.js', '--port', '11223', '--test'], shell=False,
cwd=os.path.join(current_dir, "..", "..", "nodeodm", "external", "node-OpenDroneMap"))
time.sleep(DELAY) # Wait for the server to launch
return node_odm
class TestApiTask(BootTransactionTestCase):
def setUp(self):
# We need to clear previous media_root content
@ -36,9 +46,10 @@ class TestApiTask(BootTransactionTestCase):
logger.warning("We did not remove MEDIA_ROOT because we couldn't find a _test suffix in its path.")
def test_task(self):
DELAY = 1 # time to sleep for during process launch, background processing, etc.
client = APIClient()
node_odm = start_processing_node()
user = User.objects.get(username="testuser")
self.assertFalse(user.is_superuser)
@ -55,10 +66,6 @@ class TestApiTask(BootTransactionTestCase):
other_task = Task.objects.create(project=other_project)
# Start processing node
current_dir = os.path.dirname(os.path.realpath(__file__))
node_odm = subprocess.Popen(['node', 'index.js', '--port', '11223', '--test'], shell=False,
cwd=os.path.join(current_dir, "..", "..", "nodeodm", "external", "node-OpenDroneMap"))
time.sleep(DELAY) # Wait for the server to launch
# Create processing node
pnode = ProcessingNode.objects.create(hostname="localhost", port=11223)
@ -138,9 +145,6 @@ class TestApiTask(BootTransactionTestCase):
# No processing node is set
self.assertTrue(task.processing_node is None)
image1.close()
image2.close()
# tiles.json should not be accessible at this point
res = client.get("/api/projects/{}/tasks/{}/tiles.json".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_400_BAD_REQUEST)
@ -194,6 +198,8 @@ class TestApiTask(BootTransactionTestCase):
self.assertTrue(task.status == status_codes.RUNNING)
self.assertTrue(len(task.uuid) > 0)
time.sleep(DELAY)
# Calling process pending tasks should finish the process
scheduler.process_pending_tasks()
task.refresh_from_db()
@ -249,10 +255,58 @@ class TestApiTask(BootTransactionTestCase):
testWatch.clear()
testWatch.intercept("app.scheduler.process_pending_tasks")
# Create a task, then kill the processing node
res = client.post("/api/projects/{}/tasks/".format(project.id), {
'images': [image1, image2],
'name': 'test_task_offline',
'processing_node': pnode.id
}, format="multipart")
self.assertTrue(res.status_code == status.HTTP_201_CREATED)
task = Task.objects.get(pk=res.data['id'])
# Stop processing node
node_odm.terminate()
task.refresh_from_db()
self.assertTrue(task.last_error is None)
scheduler.process_pending_tasks()
# Processing should fail and set an error
task.refresh_from_db()
self.assertTrue(task.last_error is not None)
self.assertTrue(task.status == status_codes.FAILED)
# Now bring it back online
node_odm = start_processing_node()
# Restart
res = client.post("/api/projects/{}/tasks/{}/restart/".format(project.id, task.id))
self.assertTrue(res.status_code == status.HTTP_200_OK)
task.refresh_from_db()
self.assertTrue(task.pending_action == pending_actions.RESTART)
# After processing, the task should have restarted, and have no UUID or status
scheduler.process_pending_tasks()
task.refresh_from_db()
self.assertTrue(task.status is None)
self.assertTrue(len(task.uuid) == 0)
# Another step and it should have acquired a UUID
scheduler.process_pending_tasks()
task.refresh_from_db()
self.assertTrue(task.status is status_codes.RUNNING)
self.assertTrue(len(task.uuid) > 0)
# Another step and it should be completed
time.sleep(DELAY)
scheduler.process_pending_tasks()
task.refresh_from_db()
self.assertTrue(task.status == status_codes.COMPLETED)
# TODO: what happens when nodes go offline, or an offline node is assigned to a task
# TODO: timeout issues
# Teardown processing node
node_odm.terminate()
image1.close()
image2.close()