diff options
Diffstat (limited to 'tests/serializers/test_xml.py')
| -rw-r--r-- | tests/serializers/test_xml.py | 55 |
1 files changed, 54 insertions, 1 deletions
diff --git a/tests/serializers/test_xml.py b/tests/serializers/test_xml.py index c9df2f2a5b..03462cfed5 100644 --- a/tests/serializers/test_xml.py +++ b/tests/serializers/test_xml.py @@ -1,7 +1,10 @@ +import gc +import time from xml.dom import minidom from django.core import serializers -from django.core.serializers.xml_serializer import DTDForbidden +from django.core.serializers.xml_serializer import Deserializer, DTDForbidden +from django.db import models from django.test import TestCase, TransactionTestCase from .tests import SerializersTestBase, SerializersTransactionTestBase @@ -90,6 +93,56 @@ class XmlSerializerTestCase(SerializersTestBase, TestCase): with self.assertRaises(DTDForbidden): next(serializers.deserialize("xml", xml)) + def test_crafted_xml_performance(self): + """The time to process invalid inputs is not quadratic.""" + + def build_crafted_xml(depth, leaf_text_len): + nested_open = "<nested>" * depth + nested_close = "</nested>" * depth + leaf = "x" * leaf_text_len + field_content = f"{nested_open}{leaf}{nested_close}" + return f""" + <django-objects version="1.0"> + <object model="contenttypes.contenttype" pk="1"> + <field name="app_label">{field_content}</field> + <field name="model">m</field> + </object> + </django-objects> + """ + + def deserialize(crafted_xml): + iterator = Deserializer(crafted_xml) + gc.collect() + + start_time = time.perf_counter() + result = list(iterator) + end_time = time.perf_counter() + + self.assertEqual(len(result), 1) + self.assertIsInstance(result[0].object, models.Model) + return end_time - start_time + + def assertFactor(label, params, factor=2): + factors = [] + prev_time = None + for depth, length in params: + crafted_xml = build_crafted_xml(depth, length) + elapsed = deserialize(crafted_xml) + if prev_time is not None: + factors.append(elapsed / prev_time) + prev_time = elapsed + + with self.subTest(label): + # Assert based on the average factor to reduce test flakiness. + self.assertLessEqual(sum(factors) / len(factors), factor) + + assertFactor( + "varying depth, varying length", + [(50, 2000), (100, 4000), (200, 8000), (400, 16000), (800, 32000)], + 2, + ) + assertFactor("constant depth, varying length", [(100, 1), (100, 1000)], 2) + class XmlSerializerTransactionTestCase( SerializersTransactionTestBase, TransactionTestCase |
