"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""

import datetime
import ipaddress
import sys
from decimal import Decimal

import pytest
from tcolorpy import tcolor
from typepy import (
    Bool,
    DateTime,
    Dictionary,
    Integer,
    Nan,
    RealNumber,
    StrictLevel,
    String,
    Typecode,
)

from dataproperty import (
    MIN_STRICT_LEVEL_MAP,
    Align,
    DataProperty,
    DefaultValue,
    Format,
    LineBreakHandling,
    Preprocessor,
)

from .common import get_strict_level_map


dateutil = pytest.importorskip("dateutil", minversion="2.7")

DATATIME_DATA = datetime.datetime(2017, 1, 2, 3, 4, 5)

nan = float("nan")
inf = float("inf")


class Test_DataPeroperty_eq:
    @pytest.mark.parametrize(
        ["lhs", "rhs", "expected"],
        [
            [1, 1, True],
            [1, 2, False],
            [1, 0.1, False],
            [1, True, False],
            [1.1, 1.1, True],
            [1, nan, False],
            [nan, nan, True],
            [None, None, True],
        ],
    )
    def test_normal(self, lhs, rhs, expected):
        lhs = DataProperty(lhs)
        rhs = DataProperty(rhs)

        assert (lhs == rhs) == expected
        assert (lhs != rhs) == (not expected)


class Test_DataPeroperty_data_typecode:
    @pytest.mark.parametrize(
        ["value", "expected_data", "expected_typecode"],
        [
            ["-0.00284241876820074", Decimal("-0.00284241876820074"), Typecode.REAL_NUMBER],
        ],
    )
    def test_normal(self, value, expected_data, expected_typecode):
        dp = DataProperty(value)

        assert dp == dp
        assert dp.data == expected_data
        assert dp.typecode == expected_typecode

    @pytest.mark.parametrize(
        ["value", "is_convert", "expected_data", "expected_typecode"],
        [
            [1.0, True, 1, Typecode.INTEGER],
            [sys.maxsize, True, sys.maxsize, Typecode.INTEGER],
            [-sys.maxsize, False, -sys.maxsize, Typecode.INTEGER],
            [str(-sys.maxsize), True, -sys.maxsize, Typecode.INTEGER],
            [str(sys.maxsize), False, str(sys.maxsize), Typecode.STRING],
            [1.1, True, 1, Typecode.INTEGER],
            [-1.1, False, Decimal("-1.1"), Typecode.REAL_NUMBER],
            [Decimal("1.1"), False, Decimal("1.1"), Typecode.REAL_NUMBER],
            ["1.1", True, 1, Typecode.INTEGER],
            ["-1.1", False, "-1.1", Typecode.STRING],
            ["a", True, "a", Typecode.STRING],
            ["a", False, "a", Typecode.STRING],
            ["", True, "", Typecode.NULL_STRING],
            ["", False, "", Typecode.NULL_STRING],
            [" ", True, " ", Typecode.NULL_STRING],
            [" ", False, " ", Typecode.NULL_STRING],
            ["3.3.5", True, "3.3.5", Typecode.STRING],
            ["51.0.2704.106", True, "51.0.2704.106", Typecode.STRING],
            [True, True, 1, Typecode.INTEGER],
            [False, False, False, Typecode.BOOL],
            ["100-0002", False, "100-0002", Typecode.STRING],
            ["127.0.0.1", True, ipaddress.IPv4Address("127.0.0.1"), Typecode.IP_ADDRESS],
            ["127.0.0.1", False, "127.0.0.1", Typecode.STRING],
            ["::1", True, ipaddress.IPv6Address("::1"), Typecode.IP_ADDRESS],
            ["::1", False, "::1", Typecode.STRING],
            [[], True, [], Typecode.LIST],
            [[], False, [], Typecode.LIST],
            [{}, True, {}, Typecode.DICTIONARY],
            [{}, False, {}, Typecode.DICTIONARY],
            [
                "2017-01-02 03:04:05",
                True,
                datetime.datetime(2017, 1, 2, 3, 4, 5),
                Typecode.DATETIME,
            ],
            [DATATIME_DATA, True, DATATIME_DATA, Typecode.DATETIME],
            ["2017-01-02 03:04:05", False, "2017-01-02 03:04:05", Typecode.STRING],
            [None, True, None, Typecode.NONE],
            [None, False, None, Typecode.NONE],
            ["None", True, "None", Typecode.STRING],
            ["None", False, "None", Typecode.STRING],
            [inf, True, inf, Typecode.INFINITY],
            [inf, False, Decimal(inf), Typecode.INFINITY],
            ["inf", True, Decimal(inf), Typecode.INFINITY],
            ["inf", False, "inf", Typecode.STRING],
            ["nan", False, "nan", Typecode.STRING],
            [
                "Høgskolen i Østfold er et eksempel...",
                True,
                "Høgskolen i Østfold er et eksempel...",
                Typecode.STRING,
            ],
            [
                "Høgskolen i Østfold er et eksempel...",
                False,
                "Høgskolen i Østfold er et eksempel...",
                Typecode.STRING,
            ],
            [
                "新しいテキスト ドキュメント.txt".encode(),
                True,
                "新しいテキスト ドキュメント.txt",
                Typecode.STRING,
            ],
        ],
    )
    def test_normal_strict_map(self, value, is_convert, expected_data, expected_typecode):
        dp = DataProperty(value, strict_level_map=get_strict_level_map(not is_convert))

        assert dp == dp
        assert dp != DataProperty("test for __ne__")
        assert dp.data == expected_data
        assert dp.typecode == expected_typecode

    @pytest.mark.parametrize(
        ["value", "strip_str", "is_strict", "expected_data", "expected_typecode"],
        [
            ['"1"', '"', False, 1, Typecode.INTEGER],
            ['"1"', "", False, '"1"', Typecode.STRING],
            ['"1"', '"', True, "1", Typecode.STRING],
            ['"1"', "", False, '"1"', Typecode.STRING],
            [b"hoge", "a", False, "hoge", Typecode.STRING],
            ["hogea", b"a", False, "hoge", Typecode.STRING],
        ],
    )
    def test_normal_strip_str(self, value, strip_str, is_strict, expected_data, expected_typecode):
        dp = DataProperty(
            value,
            preprocessor=Preprocessor(strip_str=strip_str),
            strict_level_map=get_strict_level_map(is_strict),
        )

        assert dp.data == expected_data
        assert dp.typecode == expected_typecode

    @pytest.mark.parametrize(
        ["value", "type_hint", "strict_level", "expected_typecode"],
        [
            ["2017-01-02 03:04:05", None, StrictLevel.MIN, Typecode.DATETIME],
            ["2017-01-02 03:04:05", None, StrictLevel.MAX, Typecode.STRING],
            ["2017-01-02 03:04:05", DateTime, StrictLevel.MIN, Typecode.DATETIME],
            ["2017-01-02 03:04:05", DateTime, StrictLevel.MAX, Typecode.DATETIME],
            ["2017-01-02 03:04:05", Integer, StrictLevel.MIN, Typecode.DATETIME],
            ["2017-01-02 03:04:05", Integer, StrictLevel.MAX, Typecode.STRING],
            ["1,000,000,000,000", None, StrictLevel.MAX, Typecode.STRING],
            ["1,000,000,000,000", None, StrictLevel.MIN + 1, Typecode.INTEGER],
            ["1,000,000,000,000", None, StrictLevel.MIN, Typecode.INTEGER],
            ["1,000,000,000,000", Integer, StrictLevel.MAX, Typecode.INTEGER],
            ["1,000,000,000,000", Integer, StrictLevel.MIN, Typecode.INTEGER],
            [DATATIME_DATA, None, StrictLevel.MIN, Typecode.DATETIME],
            [DATATIME_DATA, None, StrictLevel.MAX, Typecode.DATETIME],
            [DATATIME_DATA, String, StrictLevel.MIN, Typecode.STRING],
            [DATATIME_DATA, String, StrictLevel.MAX, Typecode.STRING],
            ["100-0002", None, StrictLevel.MIN, Typecode.STRING],
            ["45e76582", None, StrictLevel.MIN, Typecode.INTEGER],
            ["45e76582", None, StrictLevel.MAX, Typecode.STRING],
            ["4.5e-4", None, StrictLevel.MIN, Typecode.INTEGER],
            ["4.5e-4", None, StrictLevel.MIN + 1, Typecode.REAL_NUMBER],
            ["4.5e-4", None, StrictLevel.MAX, Typecode.STRING],
            [1, String, StrictLevel.MAX, Typecode.STRING],
            [1, String, StrictLevel.MIN, Typecode.STRING],
            [float("inf"), RealNumber, StrictLevel.MAX, Typecode.INFINITY],
            [float("inf"), RealNumber, StrictLevel.MIN, Typecode.INFINITY],
            [1, RealNumber, StrictLevel.MAX, Typecode.INTEGER],
            [1, RealNumber, StrictLevel.MIN, Typecode.INTEGER],
            [1.1, Integer, StrictLevel.MAX, Typecode.INTEGER],
            [1.1, Integer, StrictLevel.MIN, Typecode.INTEGER],
            ["true", None, StrictLevel.MAX, Typecode.STRING],
            ["false", None, StrictLevel.MAX, Typecode.STRING],
            ["true", None, StrictLevel.MIN, Typecode.BOOL],
            ["false", None, StrictLevel.MIN, Typecode.BOOL],
            ["true", Bool, StrictLevel.MIN, Typecode.BOOL],
            ["false", Bool, StrictLevel.MIN, Typecode.BOOL],
            [b"hoge", None, StrictLevel.MAX, Typecode.STRING],
            [b"hoge", None, StrictLevel.MIN, Typecode.STRING],
            ['{"foo": 10}', None, StrictLevel.MAX, Typecode.STRING],
            ['{"foo": 10}', None, StrictLevel.MIN, Typecode.DICTIONARY],
            ['{"foo": 10}', Dictionary, StrictLevel.MAX, Typecode.DICTIONARY],
            ['{"foo": 10}', Dictionary, StrictLevel.MIN, Typecode.DICTIONARY],
            [{"foo": 10}, None, StrictLevel.MAX, Typecode.DICTIONARY],
            [{"foo": 10}, None, StrictLevel.MIN, Typecode.LIST],
        ],
    )
    def test_normal_type_hint(self, value, type_hint, strict_level, expected_typecode):
        dp = DataProperty(value, type_hint=type_hint, strict_level_map={"default": strict_level})

        assert dp.typecode == expected_typecode

    @pytest.mark.parametrize(
        ["value", "is_convert", "expected_data", "expected_typecode"],
        [
            [nan, True, nan, Typecode.NAN],
            [nan, False, nan, Typecode.NAN],
            ["nan", True, nan, Typecode.NAN],
        ],
    )
    def test_normal_nan(self, value, is_convert, expected_data, expected_typecode):
        dp = DataProperty(value, strict_level_map=get_strict_level_map(not is_convert))

        assert Nan(dp.data).is_type()
        assert dp.typecode == expected_typecode


class Test_DataPeroperty_to_str:
    @pytest.mark.parametrize(
        ["value", "type_hint", "is_strict", "expected_data", "expected_str"],
        [
            [float("inf"), None, True, Decimal("inf"), "Infinity"],
            [float("inf"), None, False, Decimal("inf"), "Infinity"],
            [float("inf"), RealNumber, True, Decimal("inf"), "Infinity"],
            [float("inf"), RealNumber, False, Decimal("inf"), "Infinity"],
            [float("inf"), String, False, "inf", "inf"],
        ],
    )
    def test_normal(self, value, type_hint, is_strict, expected_data, expected_str):
        dp = DataProperty(
            value, type_hint=type_hint, strict_level_map=get_strict_level_map(is_strict)
        )

        assert dp.data == expected_data
        assert dp.to_str() == expected_str

    @pytest.mark.parametrize(
        ["value", "format_flags", "expected"], [[1234567, Format.THOUSAND_SEPARATOR, "1,234,567"]]
    )
    def test_normal_format_str(self, value, format_flags, expected):
        dp = DataProperty(value, format_flags=format_flags)

        assert dp.to_str() == expected


class Test_DataPeroperty_set_data:
    @pytest.mark.parametrize(
        ["value", "is_convert", "replace_tabs_with_spaces", "tab_length", "expected"],
        [
            ["a\tb", True, True, 2, "a  b"],
            ["\ta\t\tb\tc\t", True, True, 2, "  a    b  c  "],
            ["a\tb", True, True, 4, "a    b"],
            ["a\tb", True, False, 4, "a\tb"],
            ["a\tb", True, True, None, "a\tb"],
        ],
    )
    def test_normal_tab(self, value, is_convert, replace_tabs_with_spaces, tab_length, expected):
        preprocessor = Preprocessor(
            replace_tabs_with_spaces=replace_tabs_with_spaces,
            tab_length=tab_length,
        )
        dp = DataProperty(
            value, preprocessor=preprocessor, strict_level_map=get_strict_level_map(not is_convert)
        )

        assert dp.data == expected


class Test_DataPeroperty_is_escape_html_tag:
    @pytest.mark.parametrize(
        ["value", "is_escape_html_tag", "expected"],
        [
            [
                "<a href='https://google.com'>test</a>",
                True,
                "&lt;a href=&#x27;https://google.com&#x27;&gt;test&lt;/a&gt;",
            ],
            [
                "<a href='https://google.com'>test</a>",
                False,
                "<a href='https://google.com'>test</a>",
            ],
            [True, True, True],
        ],
    )
    def test_normal_tab(self, value, is_escape_html_tag, expected):
        dp = DataProperty(value, preprocessor=Preprocessor(is_escape_html_tag=is_escape_html_tag))

        assert dp.data == expected


class Test_DataPeroperty_float_type:
    @pytest.mark.parametrize(
        ["value", "float_type", "expected"], [[1.1, float, 1.1], [1.1, Decimal, Decimal("1.1")]]
    )
    def test_normal_tab(self, value, float_type, expected):
        dp = DataProperty(value, float_type=float_type)

        assert isinstance(dp.data, float_type)
        assert dp.data == expected


class Test_DataPeroperty_align:
    @pytest.mark.parametrize(
        ["value", "expected"],
        [
            [1, Align.RIGHT],
            [1.1, Align.RIGHT],
            ["a", Align.LEFT],
            [True, Align.LEFT],
            [DATATIME_DATA, Align.LEFT],
            [None, Align.LEFT],
            [inf, Align.LEFT],
            [nan, Align.LEFT],
        ],
    )
    def test_normal(self, value, expected):
        dp = DataProperty(value)
        assert dp.align == expected


class Test_DataPeroperty_len:
    @pytest.mark.parametrize(
        ["value", "expected_acw", "expected_len"],
        [
            [1, 1, None],
            [-1, 2, None],
            [1.0, 1, None],
            [-1.0, 2, None],
            [1.1, 3, None],
            [-1.1, 4, None],
            [12.34, 5, None],
            ["000", 1, None],
            ["123456789", 9, None],
            ["-123456789", 10, None],
            ["45e76582", 8, 8],
            ["a", 1, 1],
            ["a" * 1000, 1000, 1000],
            ["あ", 2, 1],
            [True, 4, None],
            [None, 4, None],
            [inf, 8, None],
            [nan, 3, None],
        ],
    )
    def test_normal(self, value, expected_acw, expected_len):
        dp = DataProperty(value)

        assert dp.ascii_char_width == expected_acw
        assert dp.length == expected_len

    @pytest.mark.parametrize(
        ["value", "expected_acw", "expected_len"],
        [
            [tcolor("0", color="red"), 1, 10],
            [tcolor("12.34", color="red"), 5, 14],
            [tcolor("abc", color="green"), 3, 12],
        ],
    )
    def test_normal_ascii_escape_sequence(self, value, expected_acw, expected_len):
        dp = DataProperty(value)

        assert dp.ascii_char_width == expected_acw
        assert dp.length == expected_len

    @pytest.mark.parametrize(
        ["value", "eaaw", "expected_acw", "expected_len"], [["øø", 1, 2, 2], ["øø", 2, 4, 2]]
    )
    def test_normal_eaaw(self, value, eaaw, expected_acw, expected_len):
        dp = DataProperty(value, east_asian_ambiguous_width=eaaw)

        assert dp.ascii_char_width == expected_acw
        assert dp.length == expected_len

    @pytest.mark.parametrize(["value", "expected"], [[nan, nan]])
    def test_abnormal(self, value, expected):
        Nan(DataProperty(value).length).is_type()

    @pytest.mark.parametrize(
        ["value", "eaaw", "expected"],
        [["øø", None, ValueError], ["øø", 0, ValueError], ["øø", 3, ValueError]],
    )
    def test_exception_eaaw(self, value, eaaw, expected):
        with pytest.raises(expected):
            DataProperty(value, east_asian_ambiguous_width=eaaw).ascii_char_width


class Test_DataPeroperty_is_include_ansi_escape:
    @pytest.mark.parametrize(
        ["value", "expected_acw"],
        [
            [0, False],
            [tcolor("0", color="red"), True],
            [12.34, False],
            [tcolor("12.34", color="red"), True],
            ["abc", False],
            [tcolor("abc", color="green"), True],
        ],
    )
    def test_normal(self, value, expected_acw):
        assert DataProperty(value).is_include_ansi_escape == expected_acw


class Test_DataPeroperty_line_break_handling:
    @pytest.mark.parametrize(
        ["value", "line_break_handling", "expected"],
        [
            ["a\nb", LineBreakHandling.NOP, "a\nb"],
            ["a\nb", LineBreakHandling.REPLACE, "a b"],
            ["a\nb", LineBreakHandling.ESCAPE, "a\\nb"],
            ["a\r\nb", LineBreakHandling.ESCAPE, "a\\r\\nb"],
            [123, LineBreakHandling.ESCAPE, 123],
        ],
    )
    def test_normal(self, value, line_break_handling, expected):
        preprocessor = Preprocessor(line_break_handling=line_break_handling)
        assert DataProperty(value, preprocessor=preprocessor).data == expected


class Test_DataPeroperty_line_break_repl:
    @pytest.mark.parametrize(
        ["value", "line_break_handling", "line_break_repl", "expected"],
        [
            ["a\nb", LineBreakHandling.REPLACE, "<br>", "a<br>b"],
            ["a\n\r\n\nb", LineBreakHandling.REPLACE, "<br>", "a<br><br><br>b"],
            ["a\nb", LineBreakHandling.NOP, "<br>", "a\nb"],
        ],
    )
    def test_normal(self, value, line_break_handling, line_break_repl, expected):
        preprocessor = Preprocessor(
            line_break_handling=line_break_handling, line_break_repl=line_break_repl
        )
        assert DataProperty(value, preprocessor=preprocessor).data == expected


class Test_DataPeroperty_escape_formula_injection:
    @pytest.mark.parametrize(
        ["value", "escape_formula_injection", "expected"],
        [
            ["a+b", True, "a+b"],
            ["=a+b", True, "'=a+b"],
            ["=a+b", False, "=a+b"],
            ["-a+b", True, "'-a+b"],
            ["-a+b", False, "-a+b"],
            ["+a+b", True, "'+a+b"],
            ["+a+b", False, "+a+b"],
            ["@a+b", True, "'@a+b"],
            ["@a+b", False, "@a+b"],
        ],
    )
    def test_normal(self, value, escape_formula_injection, expected):
        preprocessor = Preprocessor(is_escape_formula_injection=escape_formula_injection)
        assert DataProperty(value, preprocessor=preprocessor).data == expected

    @pytest.mark.parametrize(
        ["value", "expected"],
        [[0, 0], [None, None]],
    )
    def test_abnormal(self, value, expected):
        preprocessor = Preprocessor(is_escape_formula_injection=True)
        assert DataProperty(value, preprocessor=preprocessor).data == expected


class Test_DataPeroperty_get_padding_len:
    @pytest.mark.parametrize(
        ["value", "ascii_char_width", "expected"],
        [
            [1, 8, 8],
            ["000", 8, 8],
            ["a" * 1000, 8, 8],
            ["あ", 8, 7],
            ["あ" * 100, 8, 0],
            ["いろは", 8, 5],
            [["side", "where"], 20, 20],
            [["い" * 100, "ろは"], 8, 0],
            [["い", "ろは"], 20, 17],
            [{"1", 1}, 20, 20],
        ],
    )
    def test_normal(self, value, ascii_char_width, expected):
        assert DataProperty(value).get_padding_len(ascii_char_width) == expected

    @pytest.mark.parametrize(
        ["value", "ascii_char_width", "ambiguous_width", "expected"],
        [
            ["aøb", 4, 1, 4],
            ["aøb", 4, 2, 3],
        ],
    )
    def test_normal_east_asian_ambiguous_width(
        self, value, ascii_char_width, ambiguous_width, expected
    ):
        dp = DataProperty(value, east_asian_ambiguous_width=ambiguous_width)
        assert dp.get_padding_len(ascii_char_width) == expected


class Test_DataPeroperty_integer_digits:
    @pytest.mark.parametrize(
        ["value", "expected"],
        [
            [1, 1],
            [1.0, 1],
            [12.34, 2],
        ],
    )
    def test_normal(self, value, expected):
        dp = DataProperty(value)
        assert dp.integer_digits == expected

    @pytest.mark.parametrize(["value"], [[None], [True], [DATATIME_DATA], ["a"], [inf], [nan]])
    def test_abnormal(self, value):
        dp = DataProperty(value)
        Nan(dp.integer_digits).is_type()


class Test_DataPeroperty_decimal_places:
    @pytest.mark.parametrize(
        ["value", "expected"],
        [
            [1, 0],
            [1.0, 0],
            [1.1, 1],
            [12.34, 2],
        ],
    )
    def test_normal(self, value, expected):
        dp = DataProperty(value)
        assert dp.decimal_places == expected

    @pytest.mark.parametrize(["value"], [[None], [True], [DATATIME_DATA], ["a"], [inf], [nan]])
    def test_abnormal(self, value):
        dp = DataProperty(value)
        Nan(dp.decimal_places).is_type()


class Test_DataPeroperty_additional_format_len:
    @pytest.mark.parametrize(
        ["value", "expected"],
        [
            [2147483648, 0],
            [0, 0],
            [-1, 1],
            [-0.01, 1],
            ["2147483648", 0],
            ["1", 0],
            ["-1", 1],
            ["-0.01", 1],
            [None, 0],
            [True, 0],
            [DATATIME_DATA, 0],
            ["a", 0],
            [inf, 0],
            [nan, 0],
        ],
    )
    def test_normal(self, value, expected):
        dp = DataProperty(value)
        assert dp.additional_format_len == expected


class Test_DataPeroperty_repr:
    @pytest.mark.parametrize(
        ["value", "strict_level_map", "expected"],
        [
            ["100-0004", MIN_STRICT_LEVEL_MAP, 75],
            [{"a": 1}, DefaultValue.STRICT_LEVEL_MAP, 75],
            ["新しいテキスト ドキュメント.txt", DefaultValue.STRICT_LEVEL_MAP, 80],
        ],
    )
    def test_smoke(self, value, strict_level_map, expected):
        dp = DataProperty(value, strict_level_map=strict_level_map)
        assert len(dp.__repr__()) > expected

    @pytest.mark.parametrize(
        ["value", "strict_level_map", "expected"],
        [
            [
                0,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=0, type=INTEGER, align=right, "
                "ascii_width=1, int_digits=1, decimal_places=0, extra_len=0",
            ],
            [
                tcolor("0", color="red"),
                DefaultValue.STRICT_LEVEL_MAP,
                (
                    ("data={}, type=STRING, align=right, ".format(tcolor("0", color="red")))
                    + "ascii_width=1, length=10, int_digits=1, decimal_places=0, "
                    + "extra_len=0"
                ),
            ],
            [
                -1.0,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=-1, type=INTEGER, align=right, "
                "ascii_width=2, int_digits=1, decimal_places=0, extra_len=1",
            ],
            [
                -1.1,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=-1.1, type=REAL_NUMBER, align=right, "
                "ascii_width=4, int_digits=1, decimal_places=1, extra_len=1",
            ],
            [
                -12.234,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=-12.234, type=REAL_NUMBER, align=right, "
                "ascii_width=7, int_digits=2, decimal_places=3, extra_len=1",
            ],
            [
                0.01,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=0.01, type=REAL_NUMBER, align=right, "
                "ascii_width=4, int_digits=1, decimal_places=2, extra_len=0",
            ],
            [
                "0.000000000000001",
                DefaultValue.STRICT_LEVEL_MAP,
                "data=0.000000000000001, type=REAL_NUMBER, align=right, "
                "ascii_width=17, int_digits=1, decimal_places=15, extra_len=0",
            ],
            [
                "abcdefg",
                DefaultValue.STRICT_LEVEL_MAP,
                "data=abcdefg, type=STRING, align=left, ascii_width=7, length=7, extra_len=0",
            ],
            [
                "いろは",
                DefaultValue.STRICT_LEVEL_MAP,
                "data=いろは, type=STRING, align=left, ascii_width=6, length=3, extra_len=0",
            ],
            [
                None,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=None, type=NONE, align=left, ascii_width=4, extra_len=0",
            ],
            [
                True,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=True, type=BOOL, align=left, ascii_width=4, extra_len=0",
            ],
            [
                DATATIME_DATA,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=2017-01-02 03:04:05, type=DATETIME, align=left, "
                "ascii_width=19, extra_len=0",
            ],
            [
                "2017-01-02 03:04:05",
                DefaultValue.STRICT_LEVEL_MAP,
                "data=2017-01-02 03:04:05, type=STRING, align=left, "
                "ascii_width=19, length=19, extra_len=0",
            ],
            [
                "2017-01-02 03:04:05+0900",
                MIN_STRICT_LEVEL_MAP,
                "data=2017-01-02 03:04:05+09:00, type=DATETIME, align=left, "
                "ascii_width=24, extra_len=0",
            ],
            [
                inf,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=Infinity, type=INFINITY, align=left, ascii_width=8, extra_len=0",
            ],
            [
                nan,
                DefaultValue.STRICT_LEVEL_MAP,
                "data=NaN, type=NAN, align=left, ascii_width=3, extra_len=0",
            ],
            [
                ["side", "where"],
                DefaultValue.STRICT_LEVEL_MAP,
                "data=['side', 'where'], type=LIST, align=left, "
                "ascii_width=17, length=2, extra_len=0",
            ],
            [
                ["い", "ろは"],
                DefaultValue.STRICT_LEVEL_MAP,
                "data=['い', 'ろは'], type=LIST, align=left, ascii_width=14, length=2, extra_len=0",
            ],
        ],
    )
    def test_normal(self, value, strict_level_map, expected):
        dp = DataProperty(value, strict_level_map=strict_level_map)

        print(f"[expected] {expected}")
        print(f"[actual]   {dp}")

        assert str(dp) == expected
