Skip to content

Commit

Permalink
Merge pull request #125 from buildingSMART/fix/IVS-227_Bugfix_parseinfo
Browse files Browse the repository at this point in the history
IVS-227 - Fix segfault detection + refactor tests
  • Loading branch information
civilx64 authored Nov 12, 2024
2 parents e30993d + cb6b1b7 commit 799c1a2
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 152 deletions.
4 changes: 4 additions & 0 deletions backend/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

test-schema-tasks:
. $(VIRTUAL_ENV)/bin/activate && \
MEDIA_ROOT=./apps/ifc_validation/fixtures python3 manage.py test apps.ifc_validation.tests.tests_schema_validation_task --settings apps.ifc_validation.test_settings --debug-mode --verbosity 3
Expand Down
9 changes: 6 additions & 3 deletions backend/apps/ifc_validation/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,16 +330,19 @@ 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 (catches low-level/C++ errors)
try:
code = "import ifcopenshell; ifcopenshell.open('" + file_path + "')"
check_program = [sys.executable, '-c', code, file_path]
logger.debug(f'Command for {self.__qualname__}: {" ".join(check_program)}')
subprocess.run(check_program, check=True)

except subprocess.CalledProcessError as err:
task.mark_as_failed(err)
raise

if err.returncode < 0:
logger.debug(f'ifcopenshell.open() return error code {err.returncode} ({err})')
task.mark_as_failed(err)
raise

# retrieve IFC info
try:
Expand Down
168 changes: 168 additions & 0 deletions backend/apps/ifc_validation/tests/tests_parse_info_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
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)

149 changes: 0 additions & 149 deletions backend/apps/ifc_validation/tests_tasks.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
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 syntax_validation_subtask
from .tasks import parse_info_subtask
from .tasks import prerequisites_subtask
from .tasks import bsdd_validation_subtask

class ValidationTasksTestCase(TestCase):
Expand Down Expand Up @@ -70,151 +66,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_determine_aggregate_status_for_multiple_outcomes(self):

Expand Down

0 comments on commit 799c1a2

Please sign in to comment.