/**
 * Copyright (c) 2010-2020 Contributors to the openHAB project
 *
 * See the NOTICE file(s) distributed with this work for additional
 * information.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 */
package org.openhab.binding.knx.internal.dpt;

import static org.junit.Assert.*;

import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.openhab.core.library.types.DateTimeType;
import org.openhab.core.library.types.DecimalType;
import org.openhab.core.library.types.HSBType;
import org.openhab.core.library.types.IncreaseDecreaseType;
import org.openhab.core.library.types.OnOffType;
import org.openhab.core.library.types.OpenClosedType;
import org.openhab.core.library.types.PercentType;
import org.openhab.core.library.types.StopMoveType;
import org.openhab.core.library.types.StringType;
import org.openhab.core.library.types.UpDownType;
import org.openhab.core.types.Type;

import tuwien.auto.calimero.GroupAddress;
import tuwien.auto.calimero.datapoint.CommandDP;
import tuwien.auto.calimero.datapoint.Datapoint;
import tuwien.auto.calimero.dptxlator.DPT;
import tuwien.auto.calimero.dptxlator.DPTXlator1BitControlled;
import tuwien.auto.calimero.dptxlator.DPTXlator2ByteFloat;
import tuwien.auto.calimero.dptxlator.DPTXlator2ByteUnsigned;
import tuwien.auto.calimero.dptxlator.DPTXlator3BitControlled;
import tuwien.auto.calimero.dptxlator.DPTXlator4ByteFloat;
import tuwien.auto.calimero.dptxlator.DPTXlator4ByteSigned;
import tuwien.auto.calimero.dptxlator.DPTXlator4ByteUnsigned;
import tuwien.auto.calimero.dptxlator.DPTXlator8BitSigned;
import tuwien.auto.calimero.dptxlator.DPTXlator8BitUnsigned;
import tuwien.auto.calimero.dptxlator.DPTXlatorBoolean;
import tuwien.auto.calimero.dptxlator.DPTXlatorDate;
import tuwien.auto.calimero.dptxlator.DPTXlatorDateTime;
import tuwien.auto.calimero.dptxlator.DPTXlatorRGB;
import tuwien.auto.calimero.dptxlator.DPTXlatorSceneControl;
import tuwien.auto.calimero.dptxlator.DPTXlatorSceneNumber;
import tuwien.auto.calimero.dptxlator.DPTXlatorString;
import tuwien.auto.calimero.dptxlator.DPTXlatorTime;
import tuwien.auto.calimero.exception.KNXFormatException;

/**
 * This class provides test of the KNXCoreTyperMapper .
 * Tests datapoint types according KNX Association System Specification AS v1.07.00
 *
 * @author Kai Kreuzer
 * @author Volker Daube
 * @author Helmut Lehmeyer
 *
 */
public class KNXCoreTypeMapperTest {

    private final KNXCoreTypeMapper knxCoreTypeMapper = new KNXCoreTypeMapper();

    private TimeZone timeZoneBackup;

    @Before
    public void init() throws KNXFormatException {
        timeZoneBackup = TimeZone.getDefault();
        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
    }

    @After
    public void cleanup() {
        TimeZone.setDefault(timeZoneBackup);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toDPTid()
     *
     * @throws KNXFormatException
     */
    @Test
    public void testToDPTid() throws KNXFormatException {

        // Test mapping of org.openhab.core.library.types.OnOffType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + OnOffType.class + "\"",
                DPTXlatorBoolean.DPT_SWITCH.getID(), knxCoreTypeMapper.toDPTid(OnOffType.class));

        // Test mapping of org.openhab.core.library.types.IncreaseDecreaseType
        assertEquals(
                "knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + IncreaseDecreaseType.class + "\"",
                DPTXlator3BitControlled.DPT_CONTROL_DIMMING.getID(),
                knxCoreTypeMapper.toDPTid(IncreaseDecreaseType.class));

        // Test mapping of org.openhab.core.library.types.UpDownType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + UpDownType.class + "\"",
                DPTXlatorBoolean.DPT_UPDOWN.getID(), knxCoreTypeMapper.toDPTid(UpDownType.class));

        // Test mapping of org.openhab.core.library.types.StopMoveType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + StopMoveType.class + "\"",
                DPTXlatorBoolean.DPT_START.getID(), knxCoreTypeMapper.toDPTid(StopMoveType.class));

        // Test mapping of org.openhab.core.library.types.OpenClosedType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + OpenClosedType.class + "\"",
                DPTXlatorBoolean.DPT_WINDOW_DOOR.getID(), knxCoreTypeMapper.toDPTid(OpenClosedType.class));

        // Test mapping of org.openhab.core.library.types.PercentType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + PercentType.class + "\"",
                DPTXlator8BitUnsigned.DPT_SCALING.getID(), knxCoreTypeMapper.toDPTid(PercentType.class));

        // Test mapping of org.openhab.core.library.types.DecimalType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + DecimalType.class + "\"",
                DPTXlator2ByteFloat.DPT_TEMPERATURE.getID(), knxCoreTypeMapper.toDPTid(DecimalType.class));

        // Test mapping of org.openhab.core.library.types.DateTimeType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + DateTimeType.class + "\"",
                DPTXlatorTime.DPT_TIMEOFDAY.getID(), knxCoreTypeMapper.toDPTid(DateTimeType.class));

        // Test mapping of org.openhab.core.library.types.StringType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + StringType.class + "\"",
                DPTXlatorString.DPT_STRING_8859_1.getID(), knxCoreTypeMapper.toDPTid(StringType.class));

        // Test mapping of org.openhab.core.library.types.HSBType
        assertEquals("knxCoreTypeMapper.toDPTid returned datapoint type for class  \"" + HSBType.class + "\"",
                DPTXlatorRGB.DPT_RGB.getID(), knxCoreTypeMapper.toDPTid(HSBType.class));
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.001 DPT_SWITCH
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_001() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_SWITCH, OnOffType.class, "off", "on");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.002 DPT_BOOL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_002() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_BOOL, OnOffType.class, "false", "true");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.003 DPT_ENABLE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_003() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_ENABLE, OnOffType.class, "disable", "enable");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.004 DPT_RAMP
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_004() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_RAMP, OnOffType.class, "no ramp", "ramp");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.005 DPT_ALARM
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_005() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_ALARM, OnOffType.class, "no alarm", "alarm");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.006 DPT_BINARYVALUE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_006() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_BINARYVALUE, OnOffType.class, "low", "high");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.007 DPT_STEP
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_007() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_STEP, OnOffType.class, "decrease", "increase");

    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.008 DPT_UPDOWN
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_008() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_UPDOWN, UpDownType.class, "up", "down");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.009 DPT_UPDOWN
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_009() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_OPENCLOSE, OpenClosedType.class, "open", "close");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.010 DPT_START
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_010() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_START, StopMoveType.class, "stop", "start");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.011 DPT_STATE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_011() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_STATE, OnOffType.class, "inactive", "active");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.012 DPT_INVERT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_012() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_INVERT, OnOffType.class, "not inverted", "inverted");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.013 DPT_DIMSENDSTYLE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_013() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_DIMSENDSTYLE, OnOffType.class, "start/stop", "cyclic");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.014 DPT_INPUTSOURCE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_014() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_INPUTSOURCE, OnOffType.class, "fixed", "calculated");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.015 DPT_RESET
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_015() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_RESET, OnOffType.class, "no action", "reset");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.018 DPT_OCCUPANCY
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_018() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_OCCUPANCY, OnOffType.class, "not occupied", "occupied");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.019 DPT_WINDOW_DOOR
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_019() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_WINDOW_DOOR, OpenClosedType.class, "closed", "open");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.021
     * DPT_LOGICAL_FUNCTION
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_021() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_LOGICAL_FUNCTION, OnOffType.class, "OR", "AND");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.022 DPT_SCENE_AB
     * Remark: mapped to DecimalType
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_022() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_SCENE_AB, DecimalType.class, "0", "1");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B1" KNX ID: 1.023
     * DPT_SHUTTER_BLINDS_MODE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB1_1_023() throws KNXFormatException {
        testTypeMappingB1(DPTXlatorBoolean.DPT_SHUTTER_BLINDS_MODE, OnOffType.class, "only move up/down",
                "move up/down + step-stop");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.001 DPT_SWITCH_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_001() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_SWITCH_CONTROL, "off", "on");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.002 DPT_BOOL_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_002() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_BOOL_CONTROL, "false", "true");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.003 DPT_ENABLE_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_003() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_ENABLE_CONTROL, "disable", "enable");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.004 DPT_RAMP_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_004() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_RAMP_CONTROL, "no ramp", "ramp");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.005 DPT_ALARM_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_005() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_ALARM_CONTROL, "no alarm", "alarm");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.006 DPT_BINARY_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_006() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_BINARY_CONTROL, "low", "high");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.007 DPT_STEP_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_007() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_STEP_CONTROL, "decrease", "increase");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.008 DPT_UPDOWN_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_008() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_UPDOWN_CONTROL, "up", "down");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.009 DPT_SWITCH_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_009() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_OPENCLOSE_CONTROL, "open", "close");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.010 DPT_START_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_010() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_START_CONTROL, "stop", "start");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.011 DPT_STATE_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_011() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_STATE_CONTROL, "inactive", "active");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Datapoint Types B2" KNX ID: 2.012 DPT_INVERT_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingB2_2_012() throws KNXFormatException {
        testTypeMappingB1_Controlled(DPTXlator1BitControlled.DPT_INVERT_CONTROL, "not inverted", "inverted");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type B1U3 KNX ID: 3.007 DPT_CONTROL_DIMMING
     *
     * @throws KNXFormatException
     */
    @Test
    @Ignore
    public void testTypeMappingB1U3_3_007() throws KNXFormatException {
        testTypeMappingB1U3(DPTXlator3BitControlled.DPT_CONTROL_DIMMING, IncreaseDecreaseType.class, "decrease 5",
                "increase 5");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “8-Bit Unsigned Value" KNX ID: 5.001 DPT_SCALING
     *
     * This data type is a “Multi-state” type, according KNX spec. No exact linear conversion from value to byte(s) and
     * reverse is required, since rounding is
     * involved.
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitUnsigned_5_001() throws KNXFormatException {
        DPT dpt = DPTXlator8BitUnsigned.DPT_SCALING;

        testToTypeClass(dpt, PercentType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, PercentType.class));

        Type type = testToType(dpt, new byte[] { 0x0 }, PercentType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0x80 }, PercentType.class);
        testToDPTValue(dpt, type, "50");

        type = testToType(dpt, new byte[] { (byte) 0xFF }, PercentType.class);
        testToDPTValue(dpt, type, "100");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, PercentType.class);
        testToDPTValue(dpt, type, "100");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “8-Bit Unsigned Value" KNX ID: 5.003 DPT_ANGLE
     *
     * This data type is a “Multi-state” type, according KNX spec. No exact linear conversion from value to byte(s) and
     * reverse is required, since rounding is
     * involved.
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitUnsigned_5_003() throws KNXFormatException {
        DPT dpt = DPTXlator8BitUnsigned.DPT_ANGLE;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0x7F }, DecimalType.class);
        testToDPTValue(dpt, type, "179");

        type = testToType(dpt, new byte[] { (byte) 0x80 }, DecimalType.class);
        testToDPTValue(dpt, type, "181");

        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "360");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "360");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType()for type “8-Bit Unsigned Value" KNX ID: 5.004 DPT_PERCENT_U8
     * (previously name DPT_RelPos_Valve)
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitUnsigned_5_004() throws KNXFormatException {
        DPT dpt = DPTXlator8BitUnsigned.DPT_PERCENT_U8;

        testToTypeClass(dpt, PercentType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, PercentType.class));

        Type type = testToType(dpt, new byte[] { 0 }, PercentType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { 50 }, PercentType.class);
        testToDPTValue(dpt, type, "50");

        type = testToType(dpt, new byte[] { 100 }, PercentType.class);
        testToDPTValue(dpt, type, "100");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “8-Bit Unsigned Value" KNX ID: 5.005
     * DPT_DECIMALFACTOR
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitUnsigned_5_005() throws KNXFormatException {
        DPT dpt = DPTXlator8BitUnsigned.DPT_DECIMALFACTOR;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "255");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "255");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “8-Bit Unsigned Value" KNX ID: 5.006 DPT_TARRIF
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitUnsigned_5_006() throws KNXFormatException {
        DPT dpt = DPTXlator8BitUnsigned.DPT_TARIFF;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "255");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "255");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “8-Bit Unsigned Value" KNX ID: 5.010
     * DPT_VALUE_1_UCOUNT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitUnsigned_5_010() throws KNXFormatException {
        DPT dpt = DPTXlator8BitUnsigned.DPT_VALUE_1_UCOUNT;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "255");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "255");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “8-Bit Signed Value" KNX ID: 6.001
     * DPT_PERCENT_V8
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitSigned_6_001() throws KNXFormatException {
        DPT dpt = DPTXlator8BitSigned.DPT_PERCENT_V8;

        testToTypeClass(dpt, PercentType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, PercentType.class));

        Type type = testToType(dpt, new byte[] { 0 }, PercentType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0x64 }, PercentType.class);
        testToDPTValue(dpt, type, "100");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “8-Bit Signed Value" KNX ID: 6.010
     * DPT_VALUE_1_UCOUNT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping8BitSigned_6_010() throws KNXFormatException {
        DPT dpt = DPTXlator8BitSigned.DPT_VALUE_1_UCOUNT;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0x7F }, DecimalType.class);
        testToDPTValue(dpt, type, "127");

        type = testToType(dpt, new byte[] { (byte) 0x80 }, DecimalType.class);
        testToDPTValue(dpt, type, "-128");

        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "-1");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "-1");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.001
     * DPT_VALUE_2_UCOUNT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_001() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_VALUE_2_UCOUNT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.002 DPT_TIMEPERIOD
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_002() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_TIMEPERIOD);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.003
     * DPT_TIMEPERIOD_10
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_003() throws KNXFormatException {
        DPT dpt = DPTXlator2ByteUnsigned.DPT_TIMEPERIOD_10;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0x00, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "652800");

        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "655350");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "655350");
    }

    /**
     * KNXCoreTypeMapper tests for method typeMapper.toType() type “2-Octet Unsigned Value" KNX ID: 7.004
     * DPT_TIMEPERIOD_100
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_004() throws KNXFormatException {
        DPT dpt = DPTXlator2ByteUnsigned.DPT_TIMEPERIOD_100;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0x00, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "6528000");

        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "6553500");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "6553500");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() method typeMapper.toType() for type “2-Octet Unsigned Value"
     * KNX ID: 7.005 DPT_TIMEPERIOD_SEC
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_005() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_TIMEPERIOD_SEC);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() method typeMapper.toType() for type “2-Octet Unsigned Value"
     * KNX ID: 7.006 DPT_TIMEPERIOD_MIN
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_006() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_TIMEPERIOD_MIN);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.007
     * DPT_TIMEPERIOD_HOURS
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_007() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_TIMEPERIOD_HOURS);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.010
     * DPT_PROP_DATATYPE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_010() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_PROP_DATATYPE);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.011 DPT_LENGTH
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_011() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_LENGTH);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.012
     * DPT_ELECTRICAL_CURRENT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_012() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_ELECTRICAL_CURRENT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Unsigned Value" KNX ID: 7.013 DPT_BRIGHTNESS
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteUnsigned_7_013() throws KNXFormatException {
        testTypeMapping2ByteUnsigned(DPTXlator2ByteUnsigned.DPT_BRIGHTNESS);

    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.001. DPT_TEMPERATURE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_001() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_TEMPERATURE);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.002.
     * DPT_TEMPERATURE_DIFFERENCE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_002() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_TEMPERATURE_DIFFERENCE);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.003.
     * DPT_TEMPERATURE_GRADIENT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_003() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_TEMPERATURE_GRADIENT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.004.
     * DPT_INTENSITY_OF_LIGHT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_004() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_INTENSITY_OF_LIGHT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.005. DPT_WIND_SPEED
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_005() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_WIND_SPEED);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.006.
     * DPT_AIR_PRESSURE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_006() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_AIR_PRESSURE);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.007. DPT_HUMIDITY
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_007() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_HUMIDITY, PercentType.class);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.008. DPT_AIRQUALITY
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_008() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_AIRQUALITY);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.010.
     * DPT_TIME_DIFFERENCE1
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_010() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_TIME_DIFFERENCE1);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.011.
     * DPT_TIME_DIFFERENCE2
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_011() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_TIME_DIFFERENCE2);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.020. DPT_VOLTAGE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_020() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_VOLTAGE);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.021.
     * DPT_ELECTRICAL_CURRENT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_021() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_ELECTRICAL_CURRENT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.022.
     * DPT_POWERDENSITY
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_022() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_POWERDENSITY);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.023.
     * DPT_KELVIN_PER_PERCENT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_023() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_KELVIN_PER_PERCENT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.024. DPT_POWER
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_024() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_POWER);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.025. DPT_VOLUME_FLOW
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_025() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_VOLUME_FLOW);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.026. DPT_RAIN_AMOUNT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_026() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_RAIN_AMOUNT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.027. DPT_TEMP_F
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_027() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_TEMP_F);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “2-Octet Float Value". KNX ID: 9.028.
     * DPT_WIND_SPEED_KMH
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping2ByteFloat_9_028() throws KNXFormatException {
        testTypeMapping2ByteFloat(DPTXlator2ByteFloat.DPT_WIND_SPEED_KMH);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 10.001 DPT_TIMEOFDAY
     *
     * Test case: positive tests
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingTime_10_001() throws KNXFormatException {
        DPT dpt = DPTXlatorTime.DPT_TIMEOFDAY;

        testToTypeClass(dpt, DateTimeType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DateTimeType.class));

        // Use a too long byte array expecting that additional bytes will be ignored
        Type type = testToType(dpt, new byte[] { 0x20, 0x00, 0x00, (byte) 0xFF }, DateTimeType.class);
        testToDPTValue(dpt, type, "Mon, 00:00:00");

        /*
         * Set day to no day, 0 hours, 0 minutes and 0 seconds
         *
         */
        type = testToType(dpt, new byte[] { 0x00, 0x00, 0x00 }, DateTimeType.class);
        String today = String.format(Locale.US, "%1$ta", Calendar.getInstance());
        testToDPTValue(dpt, type, today + ", 00:00:00");

        /*
         * Set day to Monday, 0 hours, 0 minutes and 0 seconds January 5th, 1970 was a Monday
         */
        type = testToType(dpt, new byte[] { 0x20, 0x00, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Mon, 00:00:00");

        /*
         * * Set day to Tuesday, 0 hours, 0 minutes and 0 seconds January 6th, 1970 was a Tuesday
         */
        type = testToType(dpt, new byte[] { 0x40, 0, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Tue, 00:00:00");

        /*
         * Set day to Wednesday, 0 hours, 0 minutes and 0 seconds January 7th, 1970 was a Wednesday
         */
        type = testToType(dpt, new byte[] { 0x60, 0, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Wed, 00:00:00");

        /*
         * Set day to Thursday, 0 hours, 0 minutes and 0 seconds January 1st, 1970 was a Thursday
         */
        type = testToType(dpt, new byte[] { (byte) 0x80, 0, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Thu, 00:00:00");

        /*
         * Set day to Friday, 0 hours, 0 minutes and 0 seconds January 2nd, 1970 was a Friday
         */
        type = testToType(dpt, new byte[] { (byte) 0xA0, 0, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Fri, 00:00:00");

        /*
         * Set day to Saturday, 0 hours, 0 minutes and 0 seconds January 3rd, 1970 was a Saturday
         */
        type = testToType(dpt, new byte[] { (byte) 0xC0, 0, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Sat, 00:00:00");

        /*
         * Set day to Sunday, 0 hours, 0 minutes and 0 seconds January 4th, 1970 was a Sunday
         */
        type = testToType(dpt, new byte[] { (byte) 0xE0, 0, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Sun, 00:00:00");

        /*
         * Set day to Monday, 1 hour, 0 minutes and 0 seconds
         */
        type = testToType(dpt, new byte[] { 0x21, 0, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Mon, 01:00:00");

        /*
         * Set day to Monday, 0 hour, 1 minute and 0 seconds
         */
        type = testToType(dpt, new byte[] { 0x20, 1, 0 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Mon, 00:01:00");

        /*
         * Set day to Monday, 0 hour, 0 minute and 1 seconds
         */
        type = testToType(dpt, new byte[] { 0x20, 0, 1 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Mon, 00:00:01");

        /*
         * Set day to Monday, 23 hours, 59 minutes and 59 seconds
         */
        type = testToType(dpt, new byte[] { 0x37, 59, 59 }, DateTimeType.class);
        testToDPTValue(dpt, type, "Mon, 23:59:59");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 10.001 DPT_TIMEOFDAY
     *
     * Test case: Set day to Monday, 24 hours, 59 minutes and 59 seconds
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingTime_10_001_HoursOutOfBounds() throws KNXFormatException {
        DPT dpt = DPTXlatorTime.DPT_TIMEOFDAY;

        testToTypeClass(dpt, DateTimeType.class);

        assertNull("knxCoreTypeMapper.toType() should return null",
                testToType(dpt, new byte[] { 0x38, 59, 59 }, DateTimeType.class));
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 10.001 DPT_TIMEOFDAY
     *
     * Set day to Monday, 23 hours, 60 minutes and 59 seconds
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingTime_10_001_MinutesOutOfBounds() throws KNXFormatException {
        DPT dpt = DPTXlatorTime.DPT_TIMEOFDAY;

        testToTypeClass(dpt, DateTimeType.class);

        assertNull("knxCoreTypeMapper.toType() should return null",
                testToType(dpt, new byte[] { 0x37, 60, 59 }, DateTimeType.class));
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 10.001 DPT_TIMEOFDAY
     *
     * Set day to Monday, 23 hours, 59 minutes and 60 seconds
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingTime_10_001_SecondsOutOfBounds() throws KNXFormatException {
        DPT dpt = DPTXlatorTime.DPT_TIMEOFDAY;

        testToTypeClass(dpt, DateTimeType.class);

        assertNull("knxCoreTypeMapper.toType() should return null",
                testToType(dpt, new byte[] { 0x37, 59, 60 }, DateTimeType.class));
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 11.001 DPT_DATE
     *
     * Test illegal data (day and month cannot be 0) This should throw an KNXIllegalArgumentException
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingDate_11_001_ZeroDay() throws KNXFormatException {
        DPT dpt = DPTXlatorDate.DPT_DATE;

        testToTypeClass(dpt, DateTimeType.class);

        assertNull("knxCoreTypeMapper.toType() should return null",
                testToType(dpt, new byte[] { 0x00, 0x00, 0x00 }, DateTimeType.class));
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 11.001 DPT_DATE
     *
     * Test illegal day (cannot be 0) This should throw an KNXIllegalArgumentException
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingDate_11_001__DayZero() throws KNXFormatException {
        DPT dpt = DPTXlatorDate.DPT_DATE;

        testToTypeClass(dpt, DateTimeType.class);

        assertNull("knxCoreTypeMapper.toType() should return null",
                testToType(dpt, new byte[] { 0x00, 0x01, 0x00 }, DateTimeType.class));
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 11.001 DPT_DATE
     *
     * Test illegal month (cannot be 0) This should throw an KNXIllegalArgumentException
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingDate_11_001__ZeroMonth() throws KNXFormatException {
        DPT dpt = DPTXlatorDate.DPT_DATE;

        testToTypeClass(dpt, DateTimeType.class);

        assertNull("knxCoreTypeMapper.toType() should return null",
                testToType(dpt, new byte[] { 0x01, 0x00, 0x00 }, DateTimeType.class));
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Time" KNX ID: 11.001 DPT_DATE
     *
     * Test correct year evaluation according KNX spec.
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingDate_11_001() throws KNXFormatException {
        DPT dpt = DPTXlatorDate.DPT_DATE;

        testToTypeClass(dpt, DateTimeType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DateTimeType.class));

        // Use a too long byte array expecting that additional bytes will be ignored
        Type type = testToType(dpt, new byte[] { 0x01, 0x01, 0x00, (byte) 0xFF }, DateTimeType.class);
        testToDPTValue(dpt, type, "2000-01-01");

        type = testToType(dpt, new byte[] { 0x01, 0x01, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "2000-01-01");

        type = testToType(dpt, new byte[] { 0x01, 0x01, 99 }, DateTimeType.class);
        testToDPTValue(dpt, type, "1999-01-01");

        type = testToType(dpt, new byte[] { 0x01, 0x01, 90 }, DateTimeType.class);
        testToDPTValue(dpt, type, "1990-01-01");

        type = testToType(dpt, new byte[] { 0x01, 0x01, 89 }, DateTimeType.class);
        testToDPTValue(dpt, type, "2089-01-01");

        // Test roll over (which is actually not in the KNX spec)
        type = testToType(dpt, new byte[] { 31, 0x02, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "2000-03-02");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Unsigned Value" KNX ID: 12.001
     * DPT_VALUE_4_UCOUNT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteUnsigned_12_001() throws KNXFormatException {
        DPT dpt = DPTXlator4ByteUnsigned.DPT_VALUE_4_UCOUNT;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        // Use a too long byte array expecting that additional bytes will be ignored
        Type type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF },
                DecimalType.class);
        testToDPTValue(dpt, type, "4294967295");

        type = testToType(dpt, new byte[] { 0x00, 0x00, 0x00, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "4294967295");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.001 DPT_COUNT
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_001() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_COUNT);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.002 DPT_FLOWRATE
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_002() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_FLOWRATE);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.010
     * DPT_ACTIVE_ENERGY
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_010() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_ACTIVE_ENERGY);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.011
     * DPT_APPARENT_ENERGY
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_011() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_APPARENT_ENERGY);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.012
     * DPT_REACTIVE_ENERGY
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_012() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_REACTIVE_ENERGY);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.013
     * DPT_ACTIVE_ENERGY_KWH
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_013() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_ACTIVE_ENERGY_KWH);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.014
     * DPT_APPARENT_ENERGY_KVAH
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_014() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_APPARENT_ENERGY_KVAH);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.015
     * DPT_REACTIVE_ENERGY_KVARH
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_015() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_REACTIVE_ENERGY_KVARH);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Signed Value" KNX ID: 13.100 DPT_DELTA_TIME
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMapping4ByteSigned_13_100() throws KNXFormatException {
        testTypeMapping4ByteSigned(DPTXlator4ByteSigned.DPT_DELTA_TIME);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “4-Octet Float Value" KNX ID: 14
     *
     * @throws KNXFormatException
     */
    @Test
    @Ignore
    public void testTypeMapping4ByteFloat_14() throws KNXFormatException {
        Locale defaultLocale = Locale.getDefault();

        Locale[] locales = { defaultLocale, Locale.ENGLISH, Locale.GERMAN };
        DPT[] dpts = { DPTXlator4ByteFloat.DPT_ACCELERATION_ANGULAR, DPTXlator4ByteFloat.DPT_ANGLE_DEG,
                DPTXlator4ByteFloat.DPT_ELECTRIC_CURRENT, DPTXlator4ByteFloat.DPT_ELECTRIC_POTENTIAL,
                DPTXlator4ByteFloat.DPT_FREQUENCY, DPTXlator4ByteFloat.DPT_POWER };
        // Iterate over the locales
        for (Locale locale : locales) {
            // Iterate over the subtypes to be tested
            for (DPT dpt : dpts) {
                Locale.setDefault(locale);

                testToTypeClass(dpt, DecimalType.class);

                // Use a too short byte array
                assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                        testToType(dpt, new byte[] {}, DecimalType.class));

                try {
                    // Use a too long byte array expecting that additional bytes will be ignored
                    Type type = testToType(dpt,
                            new byte[] { (byte) 0x7F, (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF },
                            DecimalType.class);
                    testToDPTValue(dpt, type, "340282000000000000000000000000000000000");
                } catch (NumberFormatException nfe) {
                    fail("DptId: " + dpt.getID() + ", locale: " + locale + ", NumberFormatException. Expecting 0.0");
                }

                try {
                    Type type = testToType(dpt, new byte[] { 0x00, 0x00, 0x00, 0x00 }, DecimalType.class);
                    testToDPTValue(dpt, type, "0");
                } catch (NumberFormatException nfe) {
                    fail("DptId: " + dpt.getID() + ", locale: " + locale + ", NumberFormatException. Expecting 0.0");
                }

                try {
                    // Test the smallest positive value
                    Type type = testToType(dpt, new byte[] { 0x00, 0x00, 0x00, 0x01 }, DecimalType.class);
                    testToDPTValue(dpt, type, "0.0000000000000000000000000000000000000000000014");
                } catch (NumberFormatException nfe) {
                    fail("DptId: " + dpt.getID() + ", locale: " + locale
                            + ", NumberFormatException. Expecting 0.0000000000000000000000000000000000000000000014");
                }

                try {
                    // Test the smallest negative value
                    Type type = testToType(dpt, new byte[] { (byte) 0x80, 0x00, 0x00, 0x01 }, DecimalType.class);
                    testToDPTValue(dpt, type, "-0.0000000000000000000000000000000000000000000014");
                } catch (NumberFormatException nfe) {
                    fail("DptId: " + dpt.getID() + ", locale: " + locale
                            + ", NumberFormatException. Expecting -0.0000000000000000000000000000000000000000000014");
                }

                try {
                    // Test the maximum positive value
                    Type type = testToType(dpt, new byte[] { (byte) 0x7F, (byte) 0x7F, (byte) 0xFF, (byte) 0xFF },
                            DecimalType.class);
                    testToDPTValue(dpt, type, "340282000000000000000000000000000000000");
                } catch (NumberFormatException nfe) {
                    fail("DptId: " + dpt.getID() + ", locale: " + locale
                            + ", NumberFormatException. Expecting 340282000000000000000000000000000000000");
                }

                try {
                    // Test the maximum negative value
                    Type type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0x7F, (byte) 0xFF, (byte) 0xFF },
                            DecimalType.class);
                    testToDPTValue(dpt, type, "-340282000000000000000000000000000000000");
                } catch (NumberFormatException nfe) {
                    fail("DptId: " + dpt.getID() + ", locale: " + locale
                            + ", NumberFormatException. Expecting -340282000000000000000000000000000000000");
                }
            }
        }

        Locale.setDefault(defaultLocale);
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “String" KNX ID: 16.001 DPT_STRING_8859_1
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingString() throws KNXFormatException {
        DPT dpt = DPTXlatorString.DPT_STRING_8859_1;

        testToTypeClass(dpt, StringType.class);

        /*
         * According to spec the length of this DPT is fixed to 14 bytes.
         *
         * Test the that a too short array results in an <null> string. There should be an error logged by calimero lib
         * (V2.2.0).
         */
        Type type = testToType(dpt, new byte[] { 0x61, 0x62 }, StringType.class);
        testToDPTValue(dpt, type, null);

        /*
         * FIXME: According to spec the length of this DPT is fixed to 14 bytes. Calimero lib (V 2.2.0) isn't checking
         * this correctly and has a bug in
         * tuwien.auto.calimero.dptxlator.DPTXlatorString.toDPT(final byte[] buf, final int offset). Calimero accepts
         * any byte array larger or equal to 14 bytes
         * without error. As a result: anything less then 14 bytes and above a multiple of 14 bytes will be accepted but
         * cutoff. Even for the failed check (less
         * then 14 bytes) calimero is not throwing an exception but is logging an error which we cannot check for here.
         *
         * Test the erroneous behavior that a too long arrays result in a cutoff string. There probably won't be an
         * error logged by calimero lib (V2.2.0).
         */
        type = testToType(dpt,
                new byte[] { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F },
                StringType.class);
        testToDPTValue(dpt, type, "abcdefghijklmn");

        /*
         * Test a 14 byte array.
         */
        type = testToType(dpt,
                new byte[] { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E },
                StringType.class);
        testToDPTValue(dpt, type, "abcdefghijklmn");

        /*
         * Test that a byte array filled with 0 and correct length is resulting in an empty string.
         */
        type = testToType(dpt,
                new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
                StringType.class);
        testToDPTValue(dpt, type, "");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Scene Number" KNX ID: 17.001 DPT_SCENE_NUMBER
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingSceneNumber_17_001() throws KNXFormatException {
        DPT dpt = DPTXlatorSceneNumber.DPT_SCENE_NUMBER;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        // Use a too long byte array expecting that additional bytes will be ignored
        Type type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "63");

        type = testToType(dpt, new byte[] { 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { 0x3F }, DecimalType.class);
        testToDPTValue(dpt, type, "63");

        // Test that the 2 msb (reserved) are ignored
        type = testToType(dpt, new byte[] { (byte) 0xC0 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        // Test that the 2 msb (reserved) are ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "63");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Scene Number" KNX ID: 18.001 DPT_SCENE_CONTROL
     *
     * @throws KNXFormatException
     */
    @Test
    @Ignore
    public void testTypeMappingSceneNumber_18_001() throws KNXFormatException {
        DPT dpt = DPTXlatorSceneControl.DPT_SCENE_CONTROL;

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        // Use a too long byte array expecting that additional bytes will be ignored
        Type type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "learn 63");

        type = testToType(dpt, new byte[] { 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "activate 0");

        type = testToType(dpt, new byte[] { 0x3F }, DecimalType.class);
        testToDPTValue(dpt, type, "activate 63");

        // Test that the second msb (reserved) are ignored
        type = testToType(dpt, new byte[] { (byte) 0xC0 }, DecimalType.class);
        testToDPTValue(dpt, type, "learn 0");

        // Test that the second msb (reserved) are ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "learn 63");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Date Time" KNX ID: 19.001 DPT_DATE_TIME
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingDateTime_19_001() throws KNXFormatException {
        DPT dpt = DPTXlatorDateTime.DPT_DATE_TIME;

        testToTypeClass(dpt, DateTimeType.class);

        assertNull("knxCoreTypeMapper.toType() should return null (no-day)",
                testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }, DateTimeType.class));

        assertNull("knxCoreTypeMapper.toType() should return null (illegal date)",
                testToType(dpt, new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, DateTimeType.class));

        /*
         * Reference testcase
         * Monday, January 1st, 1900 01:02:03, Fault: Normal (no fault), Working Day: Bank day (No working day), Working
         * Day Field: valid,
         * Year Field valid, Months and Day fields valid, Day of week field valid, Hour of day, Minutes and Seconds
         * fields valid,
         * Standard Summer Time: Time = UT+X, Quality of Clock: clock without ext. sync signal
         */
        Type type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x21, 0x02, 0x03, 0x00, 0x00 }, DateTimeType.class);
        assertNotNull("Failed to get a type for the datapoint '" + dpt + "'", type);
        testToDPTValue(dpt, type, "1900-01-01 01:02:03");

        /*
         * Reference testcase + Fault: Fault => not supported
         */
        assertNull("knxCoreTypeMapper.toType() should return null (faulty clock)", testToType(dpt,
                new byte[] { 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, (byte) 0x80, 0x00 }, DateTimeType.class));

        /*
         * Reference testcase + Year Field invalid => not supported
         */
        assertNull("knxCoreTypeMapper.toType() should return null (date but no year)",
                testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, 0x10, 0x00 }, DateTimeType.class));

        /*
         * Reference testcase + Months and Day fields invalid => not supported
         */
        assertNull("knxCoreTypeMapper.toType() should return null (date but no day and month)",
                testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, 0x08, 0x00 }, DateTimeType.class));

        /*
         * Reference testcase + Year, Months and Day fields invalid
         */
        type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x21, 0x02, 0x03, 0x18, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "1970-01-01 01:02:03");

        /*
         * Reference testcase + Year , Months and Day fields invalid + Day of week field invalid
         */
        type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x21, 0x02, 0x03, 0x1C, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "1970-01-01 01:02:03");

        /*
         * Reference testcase + Year, Months and Day fields invalid + Day of week field invalid
         * Working day field invalid
         */
        type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x21, 0x02, 0x03, 0x3C, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "1970-01-01 01:02:03");

        /*
         * Reference testcase + Year Field invalid + Months and Day fields invalid + Day of week field invalid
         * Working day field invalid + Hour of day, Minutes and Seconds fields invalid
         */
        assertNull("knxCoreTypeMapper.toType() should return null (neither date nor time)",
                testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, 0x3E, 0x00 }, DateTimeType.class));

        /*
         * Reference testcase + Year, Months and Day fields invalid + Day of week field invalid
         * Working day field invalid + Hour of day, Minutes and Seconds fields invalid, Standard Summer Time: Time =
         * UT+X+1
         */
        assertNull("knxCoreTypeMapper.toType() should return null (neither date nor time, but summertime flag)",
                type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, 0x3F, 0x00 },
                        DateTimeType.class));

        /*
         * Reference testcase + day of week=Any day, Day of week field invalid
         */
        type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x04, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "1900-01-01 00:00:00");

        /*
         * Reference testcase + Day of week field invalid
         */
        type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, 0x04, 0x00 }, DateTimeType.class);
        testToDPTValue(dpt, type, "1900-01-01 00:00:00");

        /*
         * Reference testcase + day of week=Any day, Day of week field invalid, working day, working day field invalid
         */
        type = testToType(dpt, new byte[] { 0x00, 0x01, 0x01, 0x20, 0x00, 0x00, (byte) 0x60, 0x00 },
                DateTimeType.class);
        testToDPTValue(dpt, type, "1900-01-01 00:00:00");

        /*
         * December 31st, 2155 day of week=Any day, Day of week field invalid
         */
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0x0C, 0x1F, 0x17, 0x3B, 0x3B, (byte) 0x04, (byte) 0x00 },
                DateTimeType.class);
        testToDPTValue(dpt, type, "2155-12-31 23:59:59");

        /*
         * December 31st, 2155, 24:00:00, day of week=Any day, Day of week field invalid
         *
         * TODO: this test case should test for "2155-12-31 24:00:00" since that is what the (valid) KNX bytes
         * represent.
         * Nevertheless, calimero is "cheating" by using the milliseconds such that "23:59:59.999" is interpreted as
         * "23:59:59"
         * OpenHAB's DateTimeType doesn't support milliseconds (at least not when parsing from a String), hence 24:00:00
         * cannot be mapped.
         */
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0x0C, 0x1F, 0x18, 0x00, 0x00, (byte) 0x04, (byte) 0x00 },
                DateTimeType.class);
        testToDPTValue(dpt, type, "2155-12-31 23:59:59");

        /*
         * December 31st, 2014 24:00:00, day of week=Any day, Day of week field invalid
         *
         * TODO: this test case should test for "2155-12-31 24:00:00" since that is what the (valid) KNX bytes
         * represent.
         * Nevertheless, calimero is "cheating" by using the milliseconds such that "23:59:59.999" is interpreted as
         * "23:59:59"
         * OpenHAB's DateTimeType doesn't support milliseconds (at least not when parsing from a String), hence 24:00:00
         * cannot be mapped.
         */
        type = testToType(dpt, new byte[] { (byte) 0x72, 0x0C, 0x1F, 0x18, 0x00, 0x00, (byte) 0x04, (byte) 0x00 },
                DateTimeType.class);
        testToDPTValue(dpt, type, "2014-12-31 23:59:59");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “Date Time" KNX ID: 19.001 DPT_DATE_TIME
     * Testcase tests handling of Daylight Savings Time flag (DST).
     * Interpretation of DST is depending on default timezone, hence we're trying to test using
     * different timezones: default, New York, Berlin and Shanghai. Shanghai not having a DST.
     *
     * @throws KNXFormatException
     */
    @Test
    public void testTypeMappingDateTime_19_001_DST() throws KNXFormatException {
        DPT dpt = DPTXlatorDateTime.DPT_DATE_TIME;

        // 2014-07-31 00:00:00 DST flag set
        byte[] testDataDST = new byte[] { 0x72, 0x07, 0x1F, 0x00, 0x00, 0x00, (byte) 0x05, (byte) 0x00 };
        // 2014-07-31 00:00:00 DST flag cleared
        byte[] testDataNoDST = new byte[] { 0x72, 0x07, 0x1F, 0x00, 0x00, 0x00, (byte) 0x04, (byte) 0x00 };

        testToTypeClass(dpt, DateTimeType.class);

        Calendar c = Calendar.getInstance();
        c.set(2014, 7, 31);

        if (c.get(Calendar.DST_OFFSET) > 0) {
            // Should be null since we have a DST timezone but non-DST data: should be rejected
            assertNull(testToType(dpt, testDataNoDST, DateTimeType.class));

            Type type = testToType(dpt, testDataDST, DateTimeType.class);
            testToDPTValue(dpt, type, "2014-07-31 00:00:00");
        } else {
            // Should be null since we don't have a non-DST timezone but DST data: should be rejected
            assertNull(testToType(dpt, testDataDST, DateTimeType.class));

            Type type = testToType(dpt, testDataNoDST, DateTimeType.class);
            testToDPTValue(dpt, type, "2014-07-31 00:00:00");
        }
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toType() for type “3-byte RGB value" KNX ID: 232.600 DPT_Colour_RGB
     *
     * @throws KNXFormatException
     */
    @Test
    @Ignore
    public void testTypeMappingColourRGB_232_600() throws KNXFormatException {
        DPT dpt = DPTXlatorRGB.DPT_RGB;

        testToTypeClass(dpt, HSBType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, HSBType.class));

        // Use a too long byte array expecting that additional bytes will be ignored
        Type type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0 }, HSBType.class);
        testToDPTValue(dpt, type, "r:255 g:255 b:255");

        // Test max value
        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, 0 }, HSBType.class);
        testToDPTValue(dpt, type, "r:255 g:255 b:255");

        // Test min value
        type = testToType(dpt, new byte[] { 0x00, 0x00, 0x00 }, HSBType.class);
        testToDPTValue(dpt, type, "r:0 g:0 b:0");

        // Test correct interpretation of r value
        type = testToType(dpt, new byte[] { (byte) 0xff, 0x00, 0x00 }, HSBType.class);
        testToDPTValue(dpt, type, "r:255 g:0 b:0");

        // Test correct interpretation of g value
        type = testToType(dpt, new byte[] { 0x00, (byte) 0xff, 0x00 }, HSBType.class);
        testToDPTValue(dpt, type, "r:0 g:255 b:0");

        // Test correct interpretation of b value
        type = testToType(dpt, new byte[] { 0x00, 0x00, (byte) 0xff }, HSBType.class);
        testToDPTValue(dpt, type, "r:0 g:0 b:255");
    }

    /**
     * KNXCoreTypeMapper tests method typeMapper.toTypeClass()
     *
     * @param dpt
     * @param expectedClass
     * @throws KNXFormatException
     */
    private void testToTypeClass(DPT dpt, Class<? extends Type> expectedClass) throws KNXFormatException {
        Class<? extends Type> cls = knxCoreTypeMapper.toTypeClass(dpt.getID());
        assertEquals("knxCoreTypeMapper.toTypeClass returned wrong class for datapoint type \"" + dpt.getID() + "\"",
                expectedClass, cls);
    }

    /**
     * Convenience method: testing KNXCoretypeMapper for type “Datapoint Types B1"
     *
     * @throws KNXFormatException
     */
    private void testTypeMappingB1(DPT dpt, Class<? extends Type> expectedClass, String valueLow, String valueHigh)
            throws KNXFormatException {

        testToTypeClass(dpt, expectedClass);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, expectedClass));

        Type type = testToType(dpt, new byte[] { 0 }, expectedClass);
        testToDPTValue(dpt, type, valueLow);

        type = testToType(dpt, new byte[] { 1 }, expectedClass);
        testToDPTValue(dpt, type, valueHigh);

        type = testToType(dpt, new byte[] { 2 }, expectedClass);
        testToDPTValue(dpt, type, valueLow);

        type = testToType(dpt, new byte[] { 3 }, expectedClass);
        testToDPTValue(dpt, type, valueHigh);

        type = testToType(dpt, new byte[] { (byte) 0xFF }, expectedClass);
        testToDPTValue(dpt, type, valueHigh);

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, expectedClass);
        testToDPTValue(dpt, type, valueHigh);
    }

    /**
     * Convenience method: testing KNXCoretypeMapper for type “Datapoint Types B1 Controlled"
     *
     * @throws KNXFormatException
     */
    private void testTypeMappingB1_Controlled(DPT dpt, String valueLow, String valueHigh) throws KNXFormatException {

        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "0 " + valueLow);

        type = testToType(dpt, new byte[] { 1 }, DecimalType.class);
        testToDPTValue(dpt, type, "0 " + valueHigh);

        type = testToType(dpt, new byte[] { 2 }, DecimalType.class);
        testToDPTValue(dpt, type, "1 " + valueLow);

        type = testToType(dpt, new byte[] { 3 }, DecimalType.class);
        testToDPTValue(dpt, type, "1 " + valueHigh);

        type = testToType(dpt, new byte[] { (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "1 " + valueHigh);

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, 0 }, DecimalType.class);
        testToDPTValue(dpt, type, "1 " + valueHigh);
    }

    /**
     * Convenience method: testing KNXCoretypeMapper for type “Datapoint Types B1U3 Controlled"
     *
     * @throws KNXFormatException
     */
    private void testTypeMappingB1U3(DPT dpt, Class<? extends Type> expectedClass, String valueLow, String valueHigh)
            throws KNXFormatException {

        testToTypeClass(dpt, expectedClass);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, expectedClass));

        // 3 lsb set to 0 indicate a break.
        Type type = testToType(dpt, new byte[] { 0x00 }, expectedClass);
        assertEquals("knxCoreTypeMapper.toType() should return IncreaseDecreaseType.INCREASE for break messages",
                IncreaseDecreaseType.INCREASE, type);

        // 3 lsb set to 8 indicate a break.
        type = testToType(dpt, new byte[] { 0x08 }, expectedClass);
        assertEquals("knxCoreTypeMapper.toType() should return IncreaseDecreaseType.INCREASE for break messages",
                IncreaseDecreaseType.INCREASE, type);

        type = testToType(dpt, new byte[] { 0x01 }, expectedClass);
        testToDPTValue(dpt, type, valueLow);

        type = testToType(dpt, new byte[] { 0x0F }, expectedClass);
        testToDPTValue(dpt, type, valueHigh);

        // Check that additional bit (4 msb) will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF }, expectedClass);
        testToDPTValue(dpt, type, valueHigh);

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0x0F, 0 }, expectedClass);
        testToDPTValue(dpt, type, valueHigh);
    }

    /**
     * Convenience method: testing KNXCoretypeMapper for type “Datapoint Types 2-Byte Float"
     *
     * @throws KNXFormatException
     */
    private void testTypeMapping2ByteUnsigned(DPT dpt) throws KNXFormatException {
        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        Type type = testToType(dpt, new byte[] { 0x00, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "65280");

        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "65535");

        // Use a too long byte array expecting that additional bytes will be ignored
        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "65535");
    }

    /**
     * Convenience method: testing KNXCoretypeMapper for type “Datapoint Types 2-Byte Float"
     *
     * @throws KNXFormatException
     */
    private void testTypeMapping2ByteFloat(DPT dpt) throws KNXFormatException {
        testTypeMapping2ByteFloat(dpt, DecimalType.class);
    }

    private void testTypeMapping2ByteFloat(DPT dpt, Class<? extends Type> expectedClass) throws KNXFormatException {
        testToTypeClass(dpt, expectedClass);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, expectedClass));

        Type type = testToType(dpt, new byte[] { 0x00, 0x00 }, expectedClass);
        testToDPTValue(dpt, type, "0");

        if (expectedClass.equals(DecimalType.class.getClass())) {
            /*
             * Test the maximum positive value
             *
             */
            type = testToType(dpt, new byte[] { (byte) 0x7F, (byte) 0xFF }, expectedClass);
            testToDPTValue(dpt, type, "670760.96");

            type = testToType(dpt, new byte[] { (byte) 0x07, (byte) 0xFF }, expectedClass);
            testToDPTValue(dpt, type, "20.47");

            type = testToType(dpt, new byte[] { (byte) 0x87, (byte) 0xFF }, expectedClass);
            testToDPTValue(dpt, type, "-0.01");

            type = testToType(dpt, new byte[] { (byte) 0x80, (byte) 0x00 }, expectedClass);
            testToDPTValue(dpt, type, "-20.48");

            /*
             * Test the maximum negative value
             *
             */
            type = testToType(dpt, new byte[] { (byte) 0xF8, 0x00 }, expectedClass);
            testToDPTValue(dpt, type, "-671088.64");

            // Use a too long byte array expecting that additional bytes will be ignored
            type = testToType(dpt, new byte[] { (byte) 0xF8, (byte) 0x00, (byte) 0xFF }, expectedClass);
            testToDPTValue(dpt, type, "-671088.64");
        } else if (expectedClass.equals(PercentType.class.getClass())) {

            type = testToType(dpt, new byte[] { (byte) 0x00, (byte) 0x21 }, expectedClass);
            testToDPTValue(dpt, type, "0.3");

            type = testToType(dpt, new byte[] { (byte) 0x14, (byte) 0xEE }, expectedClass);
            testToDPTValue(dpt, type, "50.48");

            type = testToType(dpt, new byte[] { (byte) 0x1C, (byte) 0xE2 }, expectedClass);
            testToDPTValue(dpt, type, "100.0");
        }
    }

    /**
     * Convenience method: testing KNXCoretypeMapper for type “Datapoint Types 4-Byte Signed"
     *
     * @throws KNXFormatException
     */
    private void testTypeMapping4ByteSigned(DPT dpt) throws KNXFormatException {
        testToTypeClass(dpt, DecimalType.class);

        // Use a too short byte array
        assertNull("knxCoreTypeMapper.toType() should return null (required data length too short)",
                testToType(dpt, new byte[] {}, DecimalType.class));

        // Use a too long byte array expecting that additional bytes will be ignored
        Type type = testToType(dpt, new byte[] { (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF },
                DecimalType.class);
        testToDPTValue(dpt, type, "2147483647");

        type = testToType(dpt, new byte[] { 0x00, 0x00, 0x00, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "0");

        type = testToType(dpt, new byte[] { (byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "-1");

        type = testToType(dpt, new byte[] { (byte) 0x80, 0x00, 0x00, 0x00 }, DecimalType.class);
        testToDPTValue(dpt, type, "-2147483648");

        type = testToType(dpt, new byte[] { (byte) 0x7F, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF }, DecimalType.class);
        testToDPTValue(dpt, type, "2147483647");
    }

    /**
     * Convenience method: testing knxCoreTypeMapper.toType() method.
     * This test checks whether the returned Type Object is of the desired Class
     *
     * @param dpt requested datapoint type ({@link tuwien.auto.calimero.dptxlator.DPT})
     * @param data byte array with KNX raw data
     * @param expectedClass the desired class
     * @return the {@link Type} object matching the dpt or null
     * @throws KNXFormatException
     */
    private Type testToType(DPT dpt, byte[] data, Class<? extends Type> expectedClass) throws KNXFormatException {
        Type type = knxCoreTypeMapper.toType(createDP(dpt.getID()), data);
        if (type != null) {
            assertEquals("knxCoreTypeMapper.toType() returned object of wrong class for datapoint type \"" + dpt.getID()
                    + "\"", expectedClass, type.getClass());
        }
        return type;
    }

    /**
     * Convenience method: testing knxCoreTypeMapper.toDPTValue() method.
     * This test checks whether the returned Type Object contains the correct KNX value
     *
     * @param dpt requested datapoint type ({@link tuwien.auto.calimero.dptxlator.DPT})
     * @param type the Type object holding the value
     * @param expectedStringResult a string expected to be returned by knxCoreTypeMapper.toDPTValue()
     */
    private void testToDPTValue(DPT dpt, Type type, String expectedStringResult) {
        String value = knxCoreTypeMapper.toDPTValue(type, dpt.getID());
        assertEquals("knxCoreTypeMapper.toDPTValue() test failed for datapoint type \"" + dpt.getID() + "\"",
                expectedStringResult, value);
    }

    /**
     * Convenience method creating a Datapoint
     *
     * @param dpt datapoint type
     * @return a new CommandDP
     * @throws KNXFormatException
     */
    private Datapoint createDP(String dpt) throws KNXFormatException {
        int mainNumber = Integer.parseInt(dpt.substring(0, dpt.indexOf('.')));
        return new CommandDP(new GroupAddress("1/2/3"), "test", mainNumber, dpt);
    }

}