From e26d7584f47f3b61ff3ac9ba026ca1ad3f2eced9 Mon Sep 17 00:00:00 2001 From: rw-bsi Date: Mon, 11 Nov 2024 23:21:01 +0000 Subject: [PATCH] Fix segfault detection + refactor tests --- backend/Makefile | 4 + .../ifc_validation/checks/ifc_gherkin_rules | 2 +- backend/apps/ifc_validation/tasks.py | 9 +- .../tests/tests_parse_info_task.py | 167 ++++++++++++++++++ backend/apps/ifc_validation/tests_tasks.py | 145 --------------- 5 files changed, 178 insertions(+), 149 deletions(-) create mode 100644 backend/apps/ifc_validation/tests/tests_parse_info_task.py diff --git a/backend/Makefile b/backend/Makefile index 57f7598..6fc1fe8 100644 --- a/backend/Makefile +++ b/backend/Makefile @@ -67,6 +67,10 @@ test-tasks: . $(VIRTUAL_ENV)/bin/activate && \ MEDIA_ROOT=./apps/ifc_validation/fixtures python3 manage.py test apps.ifc_validation.tests_tasks --settings apps.ifc_validation.test_settings --debug-mode --verbosity 3 +test-parse-info-tasks: + . $(VIRTUAL_ENV)/bin/activate && \ + MEDIA_ROOT=./apps/ifc_validation/fixtures python3 manage.py test apps.ifc_validation.tests.tests_parse_info_task --settings apps.ifc_validation.test_settings --debug-mode --verbosity 3 + clean: rm -rf .dev rm -rf django_db.sqlite3 diff --git a/backend/apps/ifc_validation/checks/ifc_gherkin_rules b/backend/apps/ifc_validation/checks/ifc_gherkin_rules index 2429449..1ef3112 160000 --- a/backend/apps/ifc_validation/checks/ifc_gherkin_rules +++ b/backend/apps/ifc_validation/checks/ifc_gherkin_rules @@ -1 +1 @@ -Subproject commit 242944946b45e7aacd6a7ba0bb15c704d97caee0 +Subproject commit 1ef3112f74a575fd8fdacc6dc58b56ed1d23c6b5 diff --git a/backend/apps/ifc_validation/tasks.py b/backend/apps/ifc_validation/tasks.py index 8ac3799..8a3ee1d 100644 --- a/backend/apps/ifc_validation/tasks.py +++ b/backend/apps/ifc_validation/tasks.py @@ -330,7 +330,8 @@ def parse_info_subtask(self, prev_result, id, file_name, *args, **kwargs): task.mark_as_initiated() - # try to open IFC file (catch segfaults) + # try to open IFC file (only catch segfaults) + SEGFAULT_PROCESS_RETURNCODE = -11 try: code = "import ifcopenshell; ifcopenshell.open('" + file_path + "')" check_program = [sys.executable, '-c', code, file_path] @@ -338,8 +339,10 @@ def parse_info_subtask(self, prev_result, id, file_name, *args, **kwargs): subprocess.run(check_program, check=True) except subprocess.CalledProcessError as err: - task.mark_as_failed(err) - raise + + if err.returncode == SEGFAULT_PROCESS_RETURNCODE: + task.mark_as_failed(err) + raise # retrieve IFC info try: diff --git a/backend/apps/ifc_validation/tests/tests_parse_info_task.py b/backend/apps/ifc_validation/tests/tests_parse_info_task.py new file mode 100644 index 0000000..25ec53c --- /dev/null +++ b/backend/apps/ifc_validation/tests/tests_parse_info_task.py @@ -0,0 +1,167 @@ +import datetime + +from django.test import TestCase +from django.contrib.auth.models import User + +from apps.ifc_validation_models.models import * +from apps.ifc_validation_models.decorators import requires_django_user_context + +from ..tasks import parse_info_subtask + +class ParseInfoTasksTestCase(TestCase): + + @classmethod + def setUpTestData(cls): + + """ + Creates a SYSTEM user in the (in-memory) test database. + Runs once for the whole test case. + """ + + user = User.objects.create(id=1, username='SYSTEM', is_active=True) + user.save() + + @requires_django_user_context + def test_parse_info_task_valid_file_does_not_create_validation_outcome(self): + + request = ValidationRequest.objects.create( + file_name='valid_file.ifc', + file='valid_file.ifc', + size=280 + ) + request.mark_as_initiated() + + parse_info_subtask( + prev_result={'is_valid': True, 'reason': 'test'}, + id=request.id, + file_name=request.file_name + ) + + outcomes = ValidationOutcome.objects.all() + self.assertIsNotNone(outcomes) + self.assertEqual(len(outcomes), 0) + + @requires_django_user_context + def test_parse_info_task_invalid_version_does_not_create_validation_outcome(self): + + request = ValidationRequest.objects.create( + file_name='invalid_version.ifc', + file='invalid_version.ifc', + size=281 + ) + request.mark_as_initiated() + + parse_info_subtask( + prev_result={'is_valid': True, 'reason': 'test'}, + id=request.id, + file_name=request.file_name + ) + + outcomes = ValidationOutcome.objects.all() + self.assertIsNotNone(outcomes) + self.assertEqual(len(outcomes), 0) + + @requires_django_user_context + def test_parse_info_task_correctly_parses_properties(self): + + request = ValidationRequest.objects.create( + file_name='wall-with-opening-and-window.ifc', + file='wall-with-opening-and-window.ifc', + size=1 + ) + request.mark_as_initiated() + + parse_info_subtask( + prev_result={'is_valid': True, 'reason': 'test'}, + id=request.id, + file_name=request.file_name + ) + + model = Model.objects.all().first() + self.assertIsNotNone(model) + self.assertEqual(model.mvd, 'CoordinationView') + self.assertEqual(model.schema, 'IFC4') + self.assertEqual(model.number_of_elements, 2) + self.assertEqual(model.number_of_geometries, 4) + self.assertEqual(model.number_of_properties, 19) + + @requires_django_user_context + def test_parse_info_task_correctly_parses_date(self): + + request = ValidationRequest.objects.create( + file_name='pass_reverse_comment.ifc', + file='pass_reverse_comment.ifc', + size=1 + ) + request.mark_as_initiated() + + parse_info_subtask( + prev_result={'is_valid': True, 'reason': 'test'}, + id=request.id, + file_name=request.file_name + ) + + model = Model.objects.all().first() + self.assertIsNotNone(model) + self.assertEqual(model.date, datetime.datetime(2022, 5, 4, 8, 8, 30, tzinfo=datetime.timezone.utc)) + + @requires_django_user_context + def test_parse_info_task_correctly_parses_date_with_timezone(self): + + request = ValidationRequest.objects.create( + file_name='valid_file_with_tz.ifc', + file='valid_file_with_tz.ifc', + size=1 + ) + request.mark_as_initiated() + + parse_info_subtask( + prev_result={'is_valid': True, 'reason': 'test'}, + id=request.id, + file_name=request.file_name + ) + + model = Model.objects.all().first() + self.assertIsNotNone(model) + self.assertEqual(model.date, datetime.datetime(2023, 12, 16, 16, 20, 00, tzinfo=datetime.timezone.utc)) + + @requires_django_user_context + def test_parse_info_task_correctly_parses_authoring_tool(self): + + request = ValidationRequest.objects.create( + file_name='pass_reverse_comment.ifc', + file='pass_reverse_comment.ifc', + size=1 + ) + request.mark_as_initiated() + + parse_info_subtask( + prev_result={'is_valid': True, 'reason': 'test'}, + id=request.id, + file_name=request.file_name + ) + + model = Model.objects.all().first() + self.assertIsNotNone(model) + self.assertEquals('IfcOpenShell-0.7.0', model.produced_by.name) + self.assertEquals('0.7.0', model.produced_by.version) + + @requires_django_user_context + def test_parse_info_task_correctly_parses_missing_authoring_tool(self): + + request = ValidationRequest.objects.create( + file_name='valid_file.ifc', + file='valid_file.ifc', + size=1 + ) + request.mark_as_initiated() + + parse_info_subtask( + prev_result={'is_valid': True, 'reason': 'test'}, + id=request.id, + file_name=request.file_name + ) + + model = Model.objects.all().first() + self.assertIsNotNone(model) + self.assertIsNone(model.produced_by) diff --git a/backend/apps/ifc_validation/tests_tasks.py b/backend/apps/ifc_validation/tests_tasks.py index e97a573..456ae48 100644 --- a/backend/apps/ifc_validation/tests_tasks.py +++ b/backend/apps/ifc_validation/tests_tasks.py @@ -71,151 +71,6 @@ def test_syntax_validation_task_creates_error_validation_outcome(self): self.assertEqual(outcomes.first().outcome_code, ValidationOutcome.ValidationOutcomeCode.SYNTAX_ERROR) self.assertTrue('On line 1 column 1' in outcomes.first().observed) - @requires_django_user_context - def test_parse_info_task_does_not_create_validation_outcome(self): - - request = ValidationRequest.objects.create( - file_name='valid_file.ifc', - file='valid_file.ifc', - size=280 - ) - request.mark_as_initiated() - - parse_info_subtask( - prev_result={'is_valid': True, 'reason': 'test'}, - id=request.id, - file_name=request.file_name - ) - - outcomes = ValidationOutcome.objects.all() - self.assertIsNotNone(outcomes) - self.assertEqual(len(outcomes), 0) - - @requires_django_user_context - def test_parse_info_task_does_not_create_validation_outcome_2(self): - - request = ValidationRequest.objects.create( - file_name='invalid_version.ifc', - file='invalid_version.ifc', - size=281 - ) - request.mark_as_initiated() - - parse_info_subtask( - prev_result={'is_valid': True, 'reason': 'test'}, - id=request.id, - file_name=request.file_name - ) - - outcomes = ValidationOutcome.objects.all() - self.assertIsNotNone(outcomes) - self.assertEqual(len(outcomes), 0) - - @requires_django_user_context - def test_parse_info_task_parses_properties(self): - - request = ValidationRequest.objects.create( - file_name='wall-with-opening-and-window.ifc', - file='wall-with-opening-and-window.ifc', - size=1 - ) - request.mark_as_initiated() - - parse_info_subtask( - prev_result={'is_valid': True, 'reason': 'test'}, - id=request.id, - file_name=request.file_name - ) - - model = Model.objects.all().first() - self.assertIsNotNone(model) - self.assertEqual(model.mvd, 'CoordinationView') - self.assertEqual(model.schema, 'IFC4') - self.assertEqual(model.number_of_elements, 2) - self.assertEqual(model.number_of_geometries, 4) - self.assertEqual(model.number_of_properties, 19) - - @requires_django_user_context - def test_parse_info_task_parses_date(self): - - request = ValidationRequest.objects.create( - file_name='pass_reverse_comment.ifc', - file='pass_reverse_comment.ifc', - size=1 - ) - request.mark_as_initiated() - - parse_info_subtask( - prev_result={'is_valid': True, 'reason': 'test'}, - id=request.id, - file_name=request.file_name - ) - - model = Model.objects.all().first() - self.assertIsNotNone(model) - self.assertEqual(model.date, datetime.datetime(2022, 5, 4, 8, 8, 30, tzinfo=datetime.timezone.utc)) - - @requires_django_user_context - def test_parse_info_task_parses_date_with_timezone(self): - - request = ValidationRequest.objects.create( - file_name='valid_file_with_tz.ifc', - file='valid_file_with_tz.ifc', - size=1 - ) - request.mark_as_initiated() - - parse_info_subtask( - prev_result={'is_valid': True, 'reason': 'test'}, - id=request.id, - file_name=request.file_name - ) - - model = Model.objects.all().first() - self.assertIsNotNone(model) - self.assertEqual(model.date, datetime.datetime(2023, 12, 16, 16, 20, 00, tzinfo=datetime.timezone.utc)) - - @requires_django_user_context - def test_parse_info_task_parses_authoring_tool(self): - - request = ValidationRequest.objects.create( - file_name='pass_reverse_comment.ifc', - file='pass_reverse_comment.ifc', - size=1 - ) - request.mark_as_initiated() - - parse_info_subtask( - prev_result={'is_valid': True, 'reason': 'test'}, - id=request.id, - file_name=request.file_name - ) - - model = Model.objects.all().first() - self.assertIsNotNone(model) - self.assertEquals('IfcOpenShell-0.7.0', model.produced_by.name) - self.assertEquals('0.7.0', model.produced_by.version) - - @requires_django_user_context - def test_parse_info_task_parses_no_authoring_tool(self): - - request = ValidationRequest.objects.create( - file_name='valid_file.ifc', - file='valid_file.ifc', - size=1 - ) - request.mark_as_initiated() - - parse_info_subtask( - prev_result={'is_valid': True, 'reason': 'test'}, - id=request.id, - file_name=request.file_name - ) - - model = Model.objects.all().first() - self.assertIsNotNone(model) - self.assertIsNone(model.produced_by) - @requires_django_user_context def test_schema_validation_task_creates_passed_validation_outcome(self):