from unittest import TestCase
from a38 import fields
from a38 import models
from a38 import validation
from a38.builder import Builder
from a38.diff import Diff
from decimal import Decimal
import datetime
import io

NS = "http://ivaservizi.agenziaentrate.gov.it/docs/xsd/fatture/v1.2"


class FieldTestMixin:
    field_class = fields.Field

    def get_field(self, *args, **kw):
        f = self.field_class(*args, **kw)
        f.set_name("sample")
        return f

    def test_xmltag(self):
        # XML tag is autogenerated from the field name
        f = self.get_field()
        self.assertEqual(f.get_xmltag(), "Sample")

        # But can be overridden with the xmltag argument
        f = self.get_field(xmltag="OtherName")
        self.assertEqual(f.get_xmltag(), "OtherName")

    def test_empty(self):
        f = self.get_field()

        # Validating a field with null=False raises an error
        self.assert_validates(f, None, result=None, errors=[
            "sample: missing value",
        ])

        # But null values are tolerated outside validation, while structures
        # are being filled
        self.assertIsNone(f.clean_value(None))

        # Values set to None are skipped in XML
        self.assertIsNone(self.to_xml(f, None))

    def test_nullable(self):
        f = self.get_field(null=True)
        self.assert_validates(f, None, result=None)
        self.assertIsNone(f.clean_value(None))

    def test_construct_default(self):
        f = self.get_field()
        self.assertIsNone(f.get_construct_default())

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, "value", result="value")

    def test_default(self):
        f = self.get_field(default="default")
        self.assertEqual(f.clean_value(None), "default")
        self.assertEqual(self.to_xml(f, None), "<T><Sample>default</Sample></T>")

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertEqual(self.to_xml(f, "value"), "<T><Sample>value</Sample></T>")

    def test_to_python_none(self):
        f = self.get_field()
        self.assert_to_python_works(f, None)

    def test_diff_none(self):
        f = self.get_field()
        self.assert_diff_empty(f, None, None)

    def assert_validates(self, field, value, result, warnings=[], errors=[]):
        val = validation.Validation()
        validated = field.validate(val, value)
        self.assertEqual([str(x) for x in val.warnings], warnings)
        self.assertEqual([str(x) for x in val.errors], errors)
        self.assertEqual(validated, result)

    def assert_to_python_works(self, field, value, **kw):
        kw.setdefault("namespace", False)
        py = field.to_python(value, **kw)
        try:
            parsed = eval(py)
        except Exception as e:
            self.fail("cannot parse generated python {}: {}".format(repr(py), str(e)))
        clean = field.clean_value(parsed)
        self.assertEqual(clean, field.clean_value(value))

    def assert_diff_empty(self, field, first, second):
        """
        Check that the field diff between the two values is empty
        """
        res = Diff()
        field.diff(res, first, second)
        if res.differences:
            self.assertEqual([(d.prefix, d.field, d.first, d.second) for d in res.differences], [])

    def assert_diff(self, field, first, second, expected):
        """
        Check that the field diff between the two differing values, is as
        expected
        """
        res = Diff()
        field.diff(res, first, second)
        self.assertEqual([str(d) for d in res.differences], expected)

    def assert_field_diff(self, field, first, second):
        """
        Check that the field diff, from a non-composite field, between the two
        differing values, is as expected
        """
        self.assert_diff(field, first, None, ["sample: second is not set"])
        self.assert_diff(field, None, first, ["sample: first is not set"])
        self.assert_diff(field, second, None, ["sample: second is not set"])
        self.assert_diff(field, None, second, ["sample: first is not set"])
        self.assert_diff(field, first, second, ["sample: first: {}, second: {}".format(
            field.to_str(field.clean_value(first)),
            field.to_str(field.clean_value(second)))])

    def to_xml(self, field, value):
        """
        Serialize the field to XML. Returns None is the field generated no
        value in the XML.
        """
        builder = Builder()
        with builder.element("T"):
            field.to_xml(builder, value)
        tree = builder.get_tree()
        root = tree.getroot()
        if not list(root):
            return None
        with io.StringIO() as out:
            tree.write(out, encoding="unicode")
            return out.getvalue()

    def mkdt(self, ye, mo, da, ho, mi, se=0, tz=None):
        if tz is None:
            tz = fields.DateTimeField.tz_rome
        return tz.localize(datetime.datetime(ye, mo, da, ho, mi, se))


class TestField(FieldTestMixin, TestCase):
    pass


class TestStringField(FieldTestMixin, TestCase):
    field_class = fields.StringField

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, "value", result="value")
        self.assert_validates(f, 12, result="12")

    def test_default(self):
        f = self.get_field(default="default")
        self.assertEqual(f.clean_value(None), "default")
        self.assertEqual(self.to_xml(f, None), "<T><Sample>default</Sample></T>")

    def test_length(self):
        f = self.get_field(length=3)
        self.assert_validates(f, "va", result="va", errors=[
            "sample: 'va' should be at least 3 characters long",
        ])
        self.assert_validates(f, "valu", result="valu", errors=[
            "sample: 'valu' should be no more than 3 characters long",
        ])
        self.assert_validates(f, 1.15, result="1.15", errors=[
            "sample: '1.15' should be no more than 3 characters long",
        ])
        self.assert_validates(f, "val", result="val")
        self.assert_validates(f, 1.2, result="1.2")

    def test_min_length(self):
        f = self.get_field(min_length=3)
        self.assert_validates(f, "va", result="va", errors=[
            "sample: 'va' should be at least 3 characters long",
        ])
        self.assert_validates(f, "valu", result="valu")
        self.assert_validates(f, "val", result="val")
        self.assert_validates(f, 1.2, result="1.2")
        self.assert_validates(f, 1.15, result="1.15")

    def test_max_length(self):
        f = self.get_field(max_length=3)
        self.assert_validates(f, "v", result="v")
        self.assert_validates(f, "va", result="va")
        self.assert_validates(f, "val", result="val")
        self.assert_validates(f, "valu", result="valu", errors=[
            "sample: 'valu' should be no more than 3 characters long",
        ])

    def test_choices(self):
        f = self.get_field(choices=("A", "B"))
        self.assert_validates(f, "A", result="A")
        self.assert_validates(f, "B", result="B")
        self.assert_validates(f, "C", result="C", errors=[
            "sample: 'C' is not a valid choice for this field",
        ])
        self.assert_validates(f, "a", result="a", errors=[
            "sample: 'a' is not a valid choice for this field",
        ])
        self.assert_validates(f, None, result=None, errors=[
            "sample: missing value",
        ])

    def test_choices_nullable(self):
        f = self.get_field(choices=("A", "B"), null=True)
        self.assert_validates(f, "A", result="A")
        self.assert_validates(f, "B", result="B")
        self.assert_validates(f, None, result=None)
        self.assert_validates(f, "C", result="C", errors=[
            "sample: 'C' is not a valid choice for this field",
        ])
        self.assert_validates(f, "a", result="a", errors=[
            "sample: 'a' is not a valid choice for this field",
        ])

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, "")
        self.assert_to_python_works(f, "foo")
        self.assert_to_python_works(f, "'\"\n")
        self.assert_to_python_works(f, r"\d\t\n")

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, "", "")
        self.assert_diff_empty(f, "a", "a")
        self.assert_field_diff(f, "a", "b")

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertEqual(self.to_xml(f, "value"), "<T><Sample>value</Sample></T>")


class TestIntegerField(FieldTestMixin, TestCase):
    field_class = fields.IntegerField

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, 12, result=12)
        self.assert_validates(f, "12", result=12)
        self.assert_validates(f, 12.3, result=12)
        self.assert_validates(f, "foo", result="foo", errors=[
            "sample: invalid literal for int() with base 10: 'foo'",
        ])

    def test_default(self):
        f = self.get_field(default=7)
        self.assertEqual(f.clean_value(None), 7)
        self.assertEqual(self.to_xml(f, None), "<T><Sample>7</Sample></T>")

    def test_max_length(self):
        f = self.get_field(max_length=3)
        self.assert_validates(f, 1, result=1)
        self.assert_validates(f, 12, result=12)
        self.assert_validates(f, 123, result=123)
        self.assert_validates(f, 1234, result=1234, errors=[
            "sample: '1234' should be no more than 3 digits long",
        ])

    def test_choices(self):
        f = self.get_field(choices=(1, 2))
        self.assert_validates(f, 1, result=1)
        self.assert_validates(f, 2, result=2)
        self.assert_validates(f, 3, result=3, errors=[
            "sample: 3 is not a valid choice for this field",
        ])
        self.assert_validates(f, None, result=None, errors=[
            "sample: missing value",
        ])

    def test_choices_nullable(self):
        f = self.get_field(choices=(1, 2), null=True)
        self.assert_validates(f, 1, result=1)
        self.assert_validates(f, 2, result=2)
        self.assert_validates(f, 3, result=3, errors=[
            "sample: 3 is not a valid choice for this field",
        ])
        self.assert_validates(f, None, result=None)

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, 1)
        self.assert_to_python_works(f, 123456)
        self.assert_to_python_works(f, 3 ** 80)

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, 1, "1")
        self.assert_field_diff(f, 1, "2")

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertEqual(self.to_xml(f, 1), "<T><Sample>1</Sample></T>")


class TestDecimalField(FieldTestMixin, TestCase):
    field_class = fields.DecimalField

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, 12, result=Decimal("12.00"))
        self.assert_validates(f, "12", result=Decimal("12.00"))
        self.assert_validates(f, "12.345", result=Decimal("12.345"))
        self.assert_validates(f, "foo", result="foo", errors=[
            "sample: 'foo' cannot be converted to Decimal",
        ])

    def test_default(self):
        f = self.get_field(default="7.0")
        self.assertEqual(f.clean_value(None), Decimal("7.0"))
        self.assertEqual(self.to_xml(f, None), "<T><Sample>7.00</Sample></T>")

    def test_max_length(self):
        f = self.get_field(max_length=4)
        self.assert_validates(f, 1, result=Decimal("1.00"))
        # 12 becomes 12.00 which is 5 characters long on a max_length of 4
        self.assert_validates(f, 12, result=Decimal("12.00"), errors=[
            "sample: '12.00' should be no more than 4 digits long",
        ])

    def test_choices(self):
        f = self.get_field(choices=("1.1", "2.2"))
        self.assert_validates(f, "1.1", result=Decimal("1.1"))
        self.assert_validates(f, Decimal("2.2"), result=Decimal("2.2"))
        # 1.1 does not have an exact decimal representation
        self.assert_validates(f, 1.1, result=Decimal("1.100000000000000088817841970012523233890533447265625"), errors=[
            "sample: Decimal('1.100000000000000088817841970012523233890533447265625') is not a valid choice for this field",
        ])
        self.assert_validates(f, None, result=None, errors=[
            "sample: missing value",
        ])

    def test_choices_nullable(self):
        f = self.get_field(choices=("1.1", "2.2"), null=True)
        self.assert_validates(f, "1.1", result=Decimal("1.1"))
        self.assert_validates(f, Decimal("2.2"), result=Decimal("2.2"))
        self.assert_validates(f, None, result=None)
        # 1.1 does not have an exact decimal representation
        dec11 = Decimal(1.1)
        self.assert_validates(f, 1.1, result=dec11, errors=[
            "sample: {!r} is not a valid choice for this field".format(dec11),
        ])

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, "1.2")
        self.assert_to_python_works(f, Decimal("1.20"))

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, Decimal("1.0"), "1.0")
        self.assert_field_diff(f, "1.0001", "1.0002")

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertEqual(self.to_xml(f, "12.345"), "<T><Sample>12.34</Sample></T>")
        self.assertEqual(self.to_xml(f, "34.567"), "<T><Sample>34.57</Sample></T>")


class TestDateField(FieldTestMixin, TestCase):
    field_class = fields.DateField

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, datetime.date(2019, 1, 2), result=datetime.date(2019, 1, 2))
        self.assert_validates(f, "2019-01-02", result=datetime.date(2019, 1, 2))
        self.assert_validates(f, datetime.datetime(2019, 1, 2, 12, 30), result=datetime.date(2019, 1, 2))
        self.assert_validates(f, "foo", result="foo", errors=[
            "sample: Date 'foo' does not begin with YYYY-mm-dd",
        ])
        self.assert_validates(f, [123], result=[123], errors=[
            "sample: '[123]' is not an instance of str, datetime.date or datetime.datetime",
        ])

    def test_default(self):
        f = self.get_field(default="2019-01-02")
        self.assertEqual(f.clean_value(None), datetime.date(2019, 1, 2))
        self.assertEqual(self.to_xml(f, None), "<T><Sample>2019-01-02</Sample></T>")

    def test_choices(self):
        f = self.get_field(choices=("2019-01-01", "2019-01-02"))
        self.assert_validates(f, "2019-01-01", result=datetime.date(2019, 1, 1))
        self.assert_validates(f, "2019-01-02", result=datetime.date(2019, 1, 2))
        self.assert_validates(f, "2019-01-03", result=datetime.date(2019, 1, 3), errors=[
            "sample: datetime.date(2019, 1, 3) is not a valid choice for this field",
        ])
        self.assert_validates(f, None, result=None, errors=[
            "sample: missing value",
        ])

    def test_choices_nullable(self):
        f = self.get_field(choices=("2019-01-01", "2019-01-02"), null=True)
        self.assert_validates(f, "2019-01-01", result=datetime.date(2019, 1, 1))
        self.assert_validates(f, "2019-01-02", result=datetime.date(2019, 1, 2))
        self.assert_validates(f, "2019-01-03", result=datetime.date(2019, 1, 3), errors=[
            "sample: datetime.date(2019, 1, 3) is not a valid choice for this field",
        ])
        self.assert_validates(f, None, result=None)

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, "2019-01-03")
        self.assert_to_python_works(f, datetime.date(2019, 2, 4))

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, datetime.date(2019, 1, 1), "2019-01-01")
        self.assert_field_diff(f, datetime.date(2019, 1, 1), "2019-01-02")
        self.assert_field_diff(f, "2019-01-01", "2019-01-02")

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertEqual(self.to_xml(f, datetime.date(2019, 1, 2)), "<T><Sample>2019-01-02</Sample></T>")
        self.assertEqual(self.to_xml(f, "2019-01-02"), "<T><Sample>2019-01-02</Sample></T>")


class TestDateTimeField(FieldTestMixin, TestCase):
    field_class = fields.DateTimeField

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, datetime.datetime(2019, 1, 2, 12, 30), result=self.mkdt(2019, 1, 2, 12, 30))
        self.assert_validates(f, "2019-01-02T12:30:00", result=self.mkdt(2019, 1, 2, 12, 30))
        self.assert_validates(f, datetime.datetime(2019, 1, 2, 12, 30), result=self.mkdt(2019, 1, 2, 12, 30))
        self.assert_validates(f, "foo", result="foo", errors=[
            "sample: ISO string too short",
        ])
        self.assert_validates(f, [123], result=[123], errors=[
            "sample: '[123]' is not an instance of str, datetime.date or datetime.datetime",
        ])

    def test_default(self):
        f = self.get_field(default="2019-01-02T12:30:00")
        self.assertEqual(f.clean_value(None), self.mkdt(2019, 1, 2, 12, 30))
        self.assertEqual(self.to_xml(f, None), "<T><Sample>2019-01-02T12:30:00+01:00</Sample></T>")

    def test_choices(self):
        f = self.get_field(choices=("2019-01-01T12:00:00", "2019-01-02T12:30:00"))
        self.assert_validates(f, "2019-01-01T12:00:00", result=self.mkdt(2019, 1, 1, 12, 00))
        self.assert_validates(f, "2019-01-02T12:30:00", result=self.mkdt(2019, 1, 2, 12, 30))
        self.assert_validates(f, self.mkdt(2019, 1, 2, 12, 15), result=self.mkdt(2019, 1, 2, 12, 15), errors=[
            "sample: 2019-01-02T12:15:00+01:00 is not a valid choice for this field",
        ])
        self.assert_validates(f, None, result=None, errors=[
            "sample: missing value",
        ])

    def test_choices_nullable(self):
        f = self.get_field(choices=("2019-01-01T12:00:00", "2019-01-02T12:30:00"), null=True)
        self.assert_validates(f, "2019-01-01T12:00:00", result=self.mkdt(2019, 1, 1, 12, 00))
        self.assert_validates(f, "2019-01-02T12:30:00", result=self.mkdt(2019, 1, 2, 12, 30))
        self.assert_validates(f, None, result=None)
        self.assert_validates(f, self.mkdt(2019, 1, 2, 12, 15), result=self.mkdt(2019, 1, 2, 12, 15), errors=[
            "sample: 2019-01-02T12:15:00+01:00 is not a valid choice for this field",
        ])

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, "2019-01-03T04:05:06")
        self.assert_to_python_works(f, self.mkdt(2019, 1, 2, 3, 4, 5))

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, self.mkdt(2019, 1, 2, 3, 4, 5), "2019-01-02T03:04:05+01:00")
        self.assert_field_diff(f, self.mkdt(2019, 1, 2, 3, 4, 5), self.mkdt(2019, 1, 2, 3, 4, 6))
        self.assert_field_diff(f, self.mkdt(2019, 1, 2, 3, 4, 5), "2019-01-02T03:04:05+02:00")

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertEqual(self.to_xml(f, self.mkdt(2019, 1, 2, 12, 30)), "<T><Sample>2019-01-02T12:30:00+01:00</Sample></T>")
        self.assertEqual(self.to_xml(f, "2019-01-02T12:13:14"), "<T><Sample>2019-01-02T12:13:14+01:00</Sample></T>")


class TestProgressivoInvioField(FieldTestMixin, TestCase):
    field_class = fields.ProgressivoInvioField

    def test_construct_default(self):
        f = self.get_field()

        # The field generates always different, always increasing values
        a = f.get_construct_default()
        b = f.get_construct_default()
        c = f.get_construct_default()
        d = f.get_construct_default()
        self.assertNotEqual(a, b)
        self.assertNotEqual(a, c)
        self.assertNotEqual(a, d)
        self.assertNotEqual(b, c)
        self.assertNotEqual(b, d)
        self.assertNotEqual(c, d)
        self.assertLess(a, b)
        self.assertLess(b, c)
        self.assertLess(c, d)

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, "BFABFAF")


class Sample(models.Model):
    name = fields.StringField()
    value = fields.IntegerField()


class TestModelField(FieldTestMixin, TestCase):
    field_class = fields.ModelField

    def get_field(self, *args, **kw):
        return super().get_field(Sample, *args, **kw)

    def test_construct_default(self):
        f = self.get_field()
        value = f.get_construct_default()
        self.assertIsInstance(value, Sample)
        self.assertIsNone(value.name)
        self.assertIsNone(value.value)

    def test_empty(self):
        super().test_empty()

        # Empty models are skipped in XML
        f = self.get_field()
        self.assertIsNone(self.to_xml(f, Sample()))

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, Sample("test", 7), result=Sample("test", 7))

    def test_default(self):
        f = self.get_field(default=Sample("test", 7))
        self.assertEqual(f.clean_value(None), Sample("test", 7))
        self.assertEqual(self.to_xml(f, None), "<T><Sample><Name>test</Name><Value>7</Value></Sample></T>")

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, Sample("test", 7))

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, Sample("test", 7), Sample("test", "7"))
        self.assert_diff(f, Sample("test", 6), None, [
            "sample: second is not set",
        ])
        self.assert_diff(f, Sample("test", 6), Sample("test", 7), [
            "sample.value: first: 6, second: 7",
        ])
        self.assert_diff(f, Sample("test1", 6), Sample("test2", 7), [
            "sample.name: first: test1, second: test2",
            "sample.value: first: 6, second: 7",
        ])

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertEqual(self.to_xml(f, Sample("test", 7)), "<T><Sample><Name>test</Name><Value>7</Value></Sample></T>")


class TestModelListField(FieldTestMixin, TestCase):
    field_class = fields.ModelListField

    def get_field(self, *args, **kw):
        return super().get_field(Sample, *args, **kw)

    def test_construct_default(self):
        f = self.get_field()
        value = f.get_construct_default()
        self.assertEqual(value, [])

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, [], result=[], errors=[
            "sample: missing value",
        ])
        self.assert_validates(f, [Sample("test", 7)], result=[Sample("test", 7)])

        f = self.get_field(null=True)
        self.assert_validates(f, [], result=[])

    def test_min_num(self):
        f = self.get_field(min_num=2)
        self.assertEqual(f.get_construct_default(), [Sample(), Sample()])
        self.assertEqual(f.clean_value([Sample(), Sample(), Sample()]), [Sample(), Sample()])

        self.assert_validates(f, [Sample("test", 7)], result=[Sample("test", 7)], errors=[
            "sample: list must have at least 2 elements, but has only 1",
        ])
        self.assert_validates(f, [Sample("test", 6), Sample("test", 7)], result=[Sample("test", 6), Sample("test", 7)])

    def test_default(self):
        f = self.get_field(default=[Sample("test", 7)])
        self.assertEqual(f.clean_value(None), [Sample("test", 7)])
        self.assertEqual(self.to_xml(f, None), "<T><Sample><Name>test</Name><Value>7</Value></Sample></T>")

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, [Sample("test", 7)])

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, [], None)
        self.assert_diff_empty(f, [Sample("test", 7)], [Sample("test", "7")])
        self.assert_diff_empty(f, [Sample("test", 6)], [Sample("test", 6), None])
        self.assert_diff(f, [Sample("test", 6)], None, [
            "sample: second is not set",
        ])
        self.assert_diff(f, [Sample("test", 6)], [], [
            "sample: second is not set",
        ])
        self.assert_diff(f, [Sample("test", 6)], [Sample("test", 7)], [
            "sample.0.value: first: 6, second: 7",
        ])
        self.assert_diff(f, [Sample("test", 6)], [Sample("test", 6), Sample("test", 7)], [
            "sample: second has 1 extra element",
        ])
        self.assert_diff(f, [Sample("test", 6)], [Sample("test", 5), Sample("test", 7)], [
            "sample.0.value: first: 6, second: 5",
            "sample: second has 1 extra element",
        ])

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertIsNone(self.to_xml(f, []))
        self.assertEqual(self.to_xml(f, [Sample("test", 7)]), "<T><Sample><Name>test</Name><Value>7</Value></Sample></T>")


class TestListField(FieldTestMixin, TestCase):
    field_class = fields.ListField

    def get_field(self, *args, **kw):
        return super().get_field(fields.StringField(), *args, **kw)

    def test_construct_default(self):
        f = self.get_field()
        value = f.get_construct_default()
        self.assertEqual(value, [])

    def test_value(self):
        f = self.get_field()
        self.assert_validates(f, [], result=[], errors=[
            "sample: missing value",
        ])
        self.assert_validates(f, ["test1", "test2"], result=["test1", "test2"])

        f = self.get_field(null=True)
        self.assert_validates(f, [], result=[])

    def test_min_num(self):
        f = self.get_field(min_num=2)
        self.assertEqual(f.get_construct_default(), [None, None])
        self.assertEqual(f.clean_value([None, None, None]), [None, None])

        self.assert_validates(f, ["test1"], result=["test1"], errors=[
            "sample: list must have at least 2 elements, but has only 1",
        ])
        self.assert_validates(f, ["test1", "test2"], result=["test1", "test2"])

    def test_default(self):
        f = self.get_field(default=["test1", "test2"])
        self.assertEqual(f.clean_value(None), ["test1", "test2"])
        self.assertEqual(self.to_xml(f, None), "<T><Sample>test1</Sample><Sample>test2</Sample></T>")

    def test_to_python(self):
        f = self.get_field()
        self.assert_to_python_works(f, ["test1", "foo"])

        f = super().get_field(fields.DateTimeField())
        self.assert_to_python_works(f, [self.mkdt(2019, 1, 2, 3, 4), self.mkdt(2019, 2, 3, 4, 5)])

    def test_diff(self):
        f = self.get_field()
        self.assert_diff_empty(f, [], None)
        self.assert_diff_empty(f, ["test"], ["test"])
        self.assert_diff_empty(f, ["test"], ["test", None])
        self.assert_diff(f, ["test"], ["test1"], [
            "sample.0: first: test, second: test1",
        ])
        self.assert_diff(f, ["test"], ["test", "test"], [
            "sample: second has 1 extra element",
        ])
        self.assert_diff(f, ["test"], ["test1", "test2"], [
            "sample.0: first: test, second: test1",
            "sample: second has 1 extra element",
        ])

    def test_xml(self):
        f = self.get_field(null=True)
        self.assertIsNone(self.to_xml(f, []))
        self.assertEqual(self.to_xml(f, ["test", "foo"]), "<T><Sample>test</Sample><Sample>foo</Sample></T>")
