1. """
    
  2. A test spanning all the capabilities of all the serializers.
    
  3. 
    
  4. This class defines sample data and a dynamically generated
    
  5. test case that is capable of testing the capabilities of
    
  6. the serializers. This includes all valid data values, plus
    
  7. forward, backwards and self references.
    
  8. """
    
  9. import datetime
    
  10. import decimal
    
  11. import uuid
    
  12. 
    
  13. from django.core import serializers
    
  14. from django.db import connection, models
    
  15. from django.test import TestCase
    
  16. 
    
  17. from .models import (
    
  18.     Anchor,
    
  19.     AutoNowDateTimeData,
    
  20.     BigIntegerData,
    
  21.     BinaryData,
    
  22.     BooleanData,
    
  23.     BooleanPKData,
    
  24.     CharData,
    
  25.     CharPKData,
    
  26.     DateData,
    
  27.     DatePKData,
    
  28.     DateTimeData,
    
  29.     DateTimePKData,
    
  30.     DecimalData,
    
  31.     DecimalPKData,
    
  32.     EmailData,
    
  33.     EmailPKData,
    
  34.     ExplicitInheritBaseModel,
    
  35.     FileData,
    
  36.     FilePathData,
    
  37.     FilePathPKData,
    
  38.     FKData,
    
  39.     FKDataToField,
    
  40.     FKDataToO2O,
    
  41.     FKSelfData,
    
  42.     FKToUUID,
    
  43.     FloatData,
    
  44.     FloatPKData,
    
  45.     GenericData,
    
  46.     GenericIPAddressData,
    
  47.     GenericIPAddressPKData,
    
  48.     InheritAbstractModel,
    
  49.     InheritBaseModel,
    
  50.     IntegerData,
    
  51.     IntegerPKData,
    
  52.     Intermediate,
    
  53.     LengthModel,
    
  54.     M2MData,
    
  55.     M2MIntermediateData,
    
  56.     M2MSelfData,
    
  57.     ModifyingSaveData,
    
  58.     O2OData,
    
  59.     PositiveBigIntegerData,
    
  60.     PositiveIntegerData,
    
  61.     PositiveIntegerPKData,
    
  62.     PositiveSmallIntegerData,
    
  63.     PositiveSmallIntegerPKData,
    
  64.     SlugData,
    
  65.     SlugPKData,
    
  66.     SmallData,
    
  67.     SmallPKData,
    
  68.     Tag,
    
  69.     TextData,
    
  70.     TimeData,
    
  71.     UniqueAnchor,
    
  72.     UUIDData,
    
  73.     UUIDDefaultData,
    
  74. )
    
  75. from .tests import register_tests
    
  76. 
    
  77. # A set of functions that can be used to recreate
    
  78. # test data objects of various kinds.
    
  79. # The save method is a raw base model save, to make
    
  80. # sure that the data in the database matches the
    
  81. # exact test case.
    
  82. 
    
  83. 
    
  84. def data_create(pk, klass, data):
    
  85.     instance = klass(id=pk)
    
  86.     instance.data = data
    
  87.     models.Model.save_base(instance, raw=True)
    
  88.     return [instance]
    
  89. 
    
  90. 
    
  91. def generic_create(pk, klass, data):
    
  92.     instance = klass(id=pk)
    
  93.     instance.data = data[0]
    
  94.     models.Model.save_base(instance, raw=True)
    
  95.     for tag in data[1:]:
    
  96.         instance.tags.create(data=tag)
    
  97.     return [instance]
    
  98. 
    
  99. 
    
  100. def fk_create(pk, klass, data):
    
  101.     instance = klass(id=pk)
    
  102.     setattr(instance, "data_id", data)
    
  103.     models.Model.save_base(instance, raw=True)
    
  104.     return [instance]
    
  105. 
    
  106. 
    
  107. def m2m_create(pk, klass, data):
    
  108.     instance = klass(id=pk)
    
  109.     models.Model.save_base(instance, raw=True)
    
  110.     instance.data.set(data)
    
  111.     return [instance]
    
  112. 
    
  113. 
    
  114. def im2m_create(pk, klass, data):
    
  115.     instance = klass(id=pk)
    
  116.     models.Model.save_base(instance, raw=True)
    
  117.     return [instance]
    
  118. 
    
  119. 
    
  120. def im_create(pk, klass, data):
    
  121.     instance = klass(id=pk)
    
  122.     instance.right_id = data["right"]
    
  123.     instance.left_id = data["left"]
    
  124.     if "extra" in data:
    
  125.         instance.extra = data["extra"]
    
  126.     models.Model.save_base(instance, raw=True)
    
  127.     return [instance]
    
  128. 
    
  129. 
    
  130. def o2o_create(pk, klass, data):
    
  131.     instance = klass()
    
  132.     instance.data_id = data
    
  133.     models.Model.save_base(instance, raw=True)
    
  134.     return [instance]
    
  135. 
    
  136. 
    
  137. def pk_create(pk, klass, data):
    
  138.     instance = klass()
    
  139.     instance.data = data
    
  140.     models.Model.save_base(instance, raw=True)
    
  141.     return [instance]
    
  142. 
    
  143. 
    
  144. def inherited_create(pk, klass, data):
    
  145.     instance = klass(id=pk, **data)
    
  146.     # This isn't a raw save because:
    
  147.     #  1) we're testing inheritance, not field behavior, so none
    
  148.     #     of the field values need to be protected.
    
  149.     #  2) saving the child class and having the parent created
    
  150.     #     automatically is easier than manually creating both.
    
  151.     models.Model.save(instance)
    
  152.     created = [instance]
    
  153.     for klass in instance._meta.parents:
    
  154.         created.append(klass.objects.get(id=pk))
    
  155.     return created
    
  156. 
    
  157. 
    
  158. # A set of functions that can be used to compare
    
  159. # test data objects of various kinds
    
  160. 
    
  161. 
    
  162. def data_compare(testcase, pk, klass, data):
    
  163.     instance = klass.objects.get(id=pk)
    
  164.     if klass == BinaryData and data is not None:
    
  165.         testcase.assertEqual(
    
  166.             bytes(data),
    
  167.             bytes(instance.data),
    
  168.             "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)"
    
  169.             % (
    
  170.                 pk,
    
  171.                 repr(bytes(data)),
    
  172.                 type(data),
    
  173.                 repr(bytes(instance.data)),
    
  174.                 type(instance.data),
    
  175.             ),
    
  176.         )
    
  177.     else:
    
  178.         testcase.assertEqual(
    
  179.             data,
    
  180.             instance.data,
    
  181.             "Objects with PK=%d not equal; expected '%s' (%s), got '%s' (%s)"
    
  182.             % (
    
  183.                 pk,
    
  184.                 data,
    
  185.                 type(data),
    
  186.                 instance,
    
  187.                 type(instance.data),
    
  188.             ),
    
  189.         )
    
  190. 
    
  191. 
    
  192. def generic_compare(testcase, pk, klass, data):
    
  193.     instance = klass.objects.get(id=pk)
    
  194.     testcase.assertEqual(data[0], instance.data)
    
  195.     testcase.assertEqual(data[1:], [t.data for t in instance.tags.order_by("id")])
    
  196. 
    
  197. 
    
  198. def fk_compare(testcase, pk, klass, data):
    
  199.     instance = klass.objects.get(id=pk)
    
  200.     testcase.assertEqual(data, instance.data_id)
    
  201. 
    
  202. 
    
  203. def m2m_compare(testcase, pk, klass, data):
    
  204.     instance = klass.objects.get(id=pk)
    
  205.     testcase.assertEqual(data, [obj.id for obj in instance.data.order_by("id")])
    
  206. 
    
  207. 
    
  208. def im2m_compare(testcase, pk, klass, data):
    
  209.     klass.objects.get(id=pk)
    
  210.     # actually nothing else to check, the instance just should exist
    
  211. 
    
  212. 
    
  213. def im_compare(testcase, pk, klass, data):
    
  214.     instance = klass.objects.get(id=pk)
    
  215.     testcase.assertEqual(data["left"], instance.left_id)
    
  216.     testcase.assertEqual(data["right"], instance.right_id)
    
  217.     if "extra" in data:
    
  218.         testcase.assertEqual(data["extra"], instance.extra)
    
  219.     else:
    
  220.         testcase.assertEqual("doesn't matter", instance.extra)
    
  221. 
    
  222. 
    
  223. def o2o_compare(testcase, pk, klass, data):
    
  224.     instance = klass.objects.get(data=data)
    
  225.     testcase.assertEqual(data, instance.data_id)
    
  226. 
    
  227. 
    
  228. def pk_compare(testcase, pk, klass, data):
    
  229.     instance = klass.objects.get(data=data)
    
  230.     testcase.assertEqual(data, instance.data)
    
  231. 
    
  232. 
    
  233. def inherited_compare(testcase, pk, klass, data):
    
  234.     instance = klass.objects.get(id=pk)
    
  235.     for key, value in data.items():
    
  236.         testcase.assertEqual(value, getattr(instance, key))
    
  237. 
    
  238. 
    
  239. # Define some data types. Each data type is
    
  240. # actually a pair of functions; one to create
    
  241. # and one to compare objects of that type
    
  242. data_obj = (data_create, data_compare)
    
  243. generic_obj = (generic_create, generic_compare)
    
  244. fk_obj = (fk_create, fk_compare)
    
  245. m2m_obj = (m2m_create, m2m_compare)
    
  246. im2m_obj = (im2m_create, im2m_compare)
    
  247. im_obj = (im_create, im_compare)
    
  248. o2o_obj = (o2o_create, o2o_compare)
    
  249. pk_obj = (pk_create, pk_compare)
    
  250. inherited_obj = (inherited_create, inherited_compare)
    
  251. uuid_obj = uuid.uuid4()
    
  252. 
    
  253. test_data = [
    
  254.     # Format: (data type, PK value, Model Class, data)
    
  255.     (data_obj, 1, BinaryData, memoryview(b"\x05\xFD\x00")),
    
  256.     (data_obj, 2, BinaryData, None),
    
  257.     (data_obj, 5, BooleanData, True),
    
  258.     (data_obj, 6, BooleanData, False),
    
  259.     (data_obj, 7, BooleanData, None),
    
  260.     (data_obj, 10, CharData, "Test Char Data"),
    
  261.     (data_obj, 11, CharData, ""),
    
  262.     (data_obj, 12, CharData, "None"),
    
  263.     (data_obj, 13, CharData, "null"),
    
  264.     (data_obj, 14, CharData, "NULL"),
    
  265.     (data_obj, 15, CharData, None),
    
  266.     # (We use something that will fit into a latin1 database encoding here,
    
  267.     # because that is still the default used on many system setups.)
    
  268.     (data_obj, 16, CharData, "\xa5"),
    
  269.     (data_obj, 20, DateData, datetime.date(2006, 6, 16)),
    
  270.     (data_obj, 21, DateData, None),
    
  271.     (data_obj, 30, DateTimeData, datetime.datetime(2006, 6, 16, 10, 42, 37)),
    
  272.     (data_obj, 31, DateTimeData, None),
    
  273.     (data_obj, 40, EmailData, "[email protected]"),
    
  274.     (data_obj, 41, EmailData, None),
    
  275.     (data_obj, 42, EmailData, ""),
    
  276.     (data_obj, 50, FileData, "file:///foo/bar/whiz.txt"),
    
  277.     # (data_obj, 51, FileData, None),
    
  278.     (data_obj, 52, FileData, ""),
    
  279.     (data_obj, 60, FilePathData, "/foo/bar/whiz.txt"),
    
  280.     (data_obj, 61, FilePathData, None),
    
  281.     (data_obj, 62, FilePathData, ""),
    
  282.     (data_obj, 70, DecimalData, decimal.Decimal("12.345")),
    
  283.     (data_obj, 71, DecimalData, decimal.Decimal("-12.345")),
    
  284.     (data_obj, 72, DecimalData, decimal.Decimal("0.0")),
    
  285.     (data_obj, 73, DecimalData, None),
    
  286.     (data_obj, 74, FloatData, 12.345),
    
  287.     (data_obj, 75, FloatData, -12.345),
    
  288.     (data_obj, 76, FloatData, 0.0),
    
  289.     (data_obj, 77, FloatData, None),
    
  290.     (data_obj, 80, IntegerData, 123456789),
    
  291.     (data_obj, 81, IntegerData, -123456789),
    
  292.     (data_obj, 82, IntegerData, 0),
    
  293.     (data_obj, 83, IntegerData, None),
    
  294.     # (XX, ImageData
    
  295.     (data_obj, 95, GenericIPAddressData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"),
    
  296.     (data_obj, 96, GenericIPAddressData, None),
    
  297.     (data_obj, 110, PositiveBigIntegerData, 9223372036854775807),
    
  298.     (data_obj, 111, PositiveBigIntegerData, None),
    
  299.     (data_obj, 120, PositiveIntegerData, 123456789),
    
  300.     (data_obj, 121, PositiveIntegerData, None),
    
  301.     (data_obj, 130, PositiveSmallIntegerData, 12),
    
  302.     (data_obj, 131, PositiveSmallIntegerData, None),
    
  303.     (data_obj, 140, SlugData, "this-is-a-slug"),
    
  304.     (data_obj, 141, SlugData, None),
    
  305.     (data_obj, 142, SlugData, ""),
    
  306.     (data_obj, 150, SmallData, 12),
    
  307.     (data_obj, 151, SmallData, -12),
    
  308.     (data_obj, 152, SmallData, 0),
    
  309.     (data_obj, 153, SmallData, None),
    
  310.     (
    
  311.         data_obj,
    
  312.         160,
    
  313.         TextData,
    
  314.         """This is a long piece of text.
    
  315. It contains line breaks.
    
  316. Several of them.
    
  317. The end.""",
    
  318.     ),
    
  319.     (data_obj, 161, TextData, ""),
    
  320.     (data_obj, 162, TextData, None),
    
  321.     (data_obj, 170, TimeData, datetime.time(10, 42, 37)),
    
  322.     (data_obj, 171, TimeData, None),
    
  323.     (generic_obj, 200, GenericData, ["Generic Object 1", "tag1", "tag2"]),
    
  324.     (generic_obj, 201, GenericData, ["Generic Object 2", "tag2", "tag3"]),
    
  325.     (data_obj, 300, Anchor, "Anchor 1"),
    
  326.     (data_obj, 301, Anchor, "Anchor 2"),
    
  327.     (data_obj, 302, UniqueAnchor, "UAnchor 1"),
    
  328.     (fk_obj, 400, FKData, 300),  # Post reference
    
  329.     (fk_obj, 401, FKData, 500),  # Pre reference
    
  330.     (fk_obj, 402, FKData, None),  # Empty reference
    
  331.     (m2m_obj, 410, M2MData, []),  # Empty set
    
  332.     (m2m_obj, 411, M2MData, [300, 301]),  # Post reference
    
  333.     (m2m_obj, 412, M2MData, [500, 501]),  # Pre reference
    
  334.     (m2m_obj, 413, M2MData, [300, 301, 500, 501]),  # Pre and Post reference
    
  335.     (o2o_obj, None, O2OData, 300),  # Post reference
    
  336.     (o2o_obj, None, O2OData, 500),  # Pre reference
    
  337.     (fk_obj, 430, FKSelfData, 431),  # Pre reference
    
  338.     (fk_obj, 431, FKSelfData, 430),  # Post reference
    
  339.     (fk_obj, 432, FKSelfData, None),  # Empty reference
    
  340.     (m2m_obj, 440, M2MSelfData, []),
    
  341.     (m2m_obj, 441, M2MSelfData, []),
    
  342.     (m2m_obj, 442, M2MSelfData, [440, 441]),
    
  343.     (m2m_obj, 443, M2MSelfData, [445, 446]),
    
  344.     (m2m_obj, 444, M2MSelfData, [440, 441, 445, 446]),
    
  345.     (m2m_obj, 445, M2MSelfData, []),
    
  346.     (m2m_obj, 446, M2MSelfData, []),
    
  347.     (fk_obj, 450, FKDataToField, "UAnchor 1"),
    
  348.     (fk_obj, 451, FKDataToField, "UAnchor 2"),
    
  349.     (fk_obj, 452, FKDataToField, None),
    
  350.     (fk_obj, 460, FKDataToO2O, 300),
    
  351.     (im2m_obj, 470, M2MIntermediateData, None),
    
  352.     # testing post- and pre-references and extra fields
    
  353.     (im_obj, 480, Intermediate, {"right": 300, "left": 470}),
    
  354.     (im_obj, 481, Intermediate, {"right": 300, "left": 490}),
    
  355.     (im_obj, 482, Intermediate, {"right": 500, "left": 470}),
    
  356.     (im_obj, 483, Intermediate, {"right": 500, "left": 490}),
    
  357.     (im_obj, 484, Intermediate, {"right": 300, "left": 470, "extra": "extra"}),
    
  358.     (im_obj, 485, Intermediate, {"right": 300, "left": 490, "extra": "extra"}),
    
  359.     (im_obj, 486, Intermediate, {"right": 500, "left": 470, "extra": "extra"}),
    
  360.     (im_obj, 487, Intermediate, {"right": 500, "left": 490, "extra": "extra"}),
    
  361.     (im2m_obj, 490, M2MIntermediateData, []),
    
  362.     (data_obj, 500, Anchor, "Anchor 3"),
    
  363.     (data_obj, 501, Anchor, "Anchor 4"),
    
  364.     (data_obj, 502, UniqueAnchor, "UAnchor 2"),
    
  365.     (pk_obj, 601, BooleanPKData, True),
    
  366.     (pk_obj, 602, BooleanPKData, False),
    
  367.     (pk_obj, 610, CharPKData, "Test Char PKData"),
    
  368.     (pk_obj, 620, DatePKData, datetime.date(2006, 6, 16)),
    
  369.     (pk_obj, 630, DateTimePKData, datetime.datetime(2006, 6, 16, 10, 42, 37)),
    
  370.     (pk_obj, 640, EmailPKData, "[email protected]"),
    
  371.     # (pk_obj, 650, FilePKData, 'file:///foo/bar/whiz.txt'),
    
  372.     (pk_obj, 660, FilePathPKData, "/foo/bar/whiz.txt"),
    
  373.     (pk_obj, 670, DecimalPKData, decimal.Decimal("12.345")),
    
  374.     (pk_obj, 671, DecimalPKData, decimal.Decimal("-12.345")),
    
  375.     (pk_obj, 672, DecimalPKData, decimal.Decimal("0.0")),
    
  376.     (pk_obj, 673, FloatPKData, 12.345),
    
  377.     (pk_obj, 674, FloatPKData, -12.345),
    
  378.     (pk_obj, 675, FloatPKData, 0.0),
    
  379.     (pk_obj, 680, IntegerPKData, 123456789),
    
  380.     (pk_obj, 681, IntegerPKData, -123456789),
    
  381.     (pk_obj, 682, IntegerPKData, 0),
    
  382.     # (XX, ImagePKData
    
  383.     (pk_obj, 695, GenericIPAddressPKData, "fe80:1424:2223:6cff:fe8a:2e8a:2151:abcd"),
    
  384.     (pk_obj, 720, PositiveIntegerPKData, 123456789),
    
  385.     (pk_obj, 730, PositiveSmallIntegerPKData, 12),
    
  386.     (pk_obj, 740, SlugPKData, "this-is-a-slug"),
    
  387.     (pk_obj, 750, SmallPKData, 12),
    
  388.     (pk_obj, 751, SmallPKData, -12),
    
  389.     (pk_obj, 752, SmallPKData, 0),
    
  390.     # (pk_obj, 760, TextPKData, """This is a long piece of text.
    
  391.     # It contains line breaks.
    
  392.     # Several of them.
    
  393.     # The end."""),
    
  394.     # (pk_obj, 770, TimePKData, datetime.time(10, 42, 37)),
    
  395.     # (pk_obj, 790, XMLPKData, "<foo></foo>"),
    
  396.     (pk_obj, 791, UUIDData, uuid_obj),
    
  397.     (fk_obj, 792, FKToUUID, uuid_obj),
    
  398.     (pk_obj, 793, UUIDDefaultData, uuid_obj),
    
  399.     (data_obj, 800, AutoNowDateTimeData, datetime.datetime(2006, 6, 16, 10, 42, 37)),
    
  400.     (data_obj, 810, ModifyingSaveData, 42),
    
  401.     (inherited_obj, 900, InheritAbstractModel, {"child_data": 37, "parent_data": 42}),
    
  402.     (
    
  403.         inherited_obj,
    
  404.         910,
    
  405.         ExplicitInheritBaseModel,
    
  406.         {"child_data": 37, "parent_data": 42},
    
  407.     ),
    
  408.     (inherited_obj, 920, InheritBaseModel, {"child_data": 37, "parent_data": 42}),
    
  409.     (data_obj, 1000, BigIntegerData, 9223372036854775807),
    
  410.     (data_obj, 1001, BigIntegerData, -9223372036854775808),
    
  411.     (data_obj, 1002, BigIntegerData, 0),
    
  412.     (data_obj, 1003, BigIntegerData, None),
    
  413.     (data_obj, 1004, LengthModel, 0),
    
  414.     (data_obj, 1005, LengthModel, 1),
    
  415. ]
    
  416. 
    
  417. 
    
  418. # Because Oracle treats the empty string as NULL, Oracle is expected to fail
    
  419. # when field.empty_strings_allowed is True and the value is None; skip these
    
  420. # tests.
    
  421. if connection.features.interprets_empty_strings_as_nulls:
    
  422.     test_data = [
    
  423.         data
    
  424.         for data in test_data
    
  425.         if not (
    
  426.             data[0] == data_obj
    
  427.             and data[2]._meta.get_field("data").empty_strings_allowed
    
  428.             and data[3] is None
    
  429.         )
    
  430.     ]
    
  431. 
    
  432. 
    
  433. class SerializerDataTests(TestCase):
    
  434.     pass
    
  435. 
    
  436. 
    
  437. def serializerTest(self, format):
    
  438.     # FK to an object with PK of 0. This won't work on MySQL without the
    
  439.     # NO_AUTO_VALUE_ON_ZERO SQL mode since it won't let you create an object
    
  440.     # with an autoincrement primary key of 0.
    
  441.     if connection.features.allows_auto_pk_0:
    
  442.         test_data.extend(
    
  443.             [
    
  444.                 (data_obj, 0, Anchor, "Anchor 0"),
    
  445.                 (fk_obj, 465, FKData, 0),
    
  446.             ]
    
  447.         )
    
  448. 
    
  449.     # Create all the objects defined in the test data
    
  450.     objects = []
    
  451.     instance_count = {}
    
  452.     for func, pk, klass, datum in test_data:
    
  453.         with connection.constraint_checks_disabled():
    
  454.             objects.extend(func[0](pk, klass, datum))
    
  455. 
    
  456.     # Get a count of the number of objects created for each class
    
  457.     for klass in instance_count:
    
  458.         instance_count[klass] = klass.objects.count()
    
  459. 
    
  460.     # Add the generic tagged objects to the object list
    
  461.     objects.extend(Tag.objects.all())
    
  462. 
    
  463.     # Serialize the test database
    
  464.     serialized_data = serializers.serialize(format, objects, indent=2)
    
  465. 
    
  466.     for obj in serializers.deserialize(format, serialized_data):
    
  467.         obj.save()
    
  468. 
    
  469.     # Assert that the deserialized data is the same
    
  470.     # as the original source
    
  471.     for func, pk, klass, datum in test_data:
    
  472.         func[1](self, pk, klass, datum)
    
  473. 
    
  474.     # Assert that the number of objects deserialized is the
    
  475.     # same as the number that was serialized.
    
  476.     for klass, count in instance_count.items():
    
  477.         self.assertEqual(count, klass.objects.count())
    
  478. 
    
  479. 
    
  480. register_tests(SerializerDataTests, "test_%s_serializer", serializerTest)