// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

#include "OBDDataDecoder.h"
#include "EnumUtility.h"
#include "Testing.h"
#include "datatypes/OBDDataTypesUnitTestOnly.h"
#include <gtest/gtest.h>
#include <unistd.h>

using namespace Aws::IoTFleetWise::DataManagement;
using namespace Aws::IoTFleetWise::TestingSupport;

// For testing purpose, Signal ID is defined as PID | (signal_order << PID_SIGNAL_BITS_LEFT_SHIFT)
#define PID_SIGNAL_BITS_LEFT_SHIFT 8

class OBDDataDecoderTest : public ::testing::Test
{
protected:
    std::shared_ptr<OBDDecoderDictionary> decoderDictPtr;
    OBDDataDecoder decoder{ decoderDictPtr };
    void
    SetUp() override
    {
        decoderDictPtr = std::make_shared<OBDDecoderDictionary>();
        // In final product, signal ID comes from Cloud, edge doesn't generate signal ID
        // Below signal ID initialization got implemented only for Edge testing.
        // In this test, signal ID are defined as PID | (signal order) << 8
        for ( PID pid = toUType( EmissionPIDs::FUEL_SYSTEM_STATUS ); pid <= toUType( EmissionPIDs::ODOMETER ); ++pid )
        {
            // Initialize decoder dictionary based on mode1PIDs table from OBDDataDecoder module
            // Note in actual product, the decoder dictionary comes from decoder manifest
            CANMessageFormat format;
            format.mMessageID = pid;
            format.mSizeInBytes = static_cast<uint8_t>( mode1PIDs[pid].retLen );
            format.mSignals = std::vector<CANSignalFormat>( mode1PIDs[pid].formulas.size() );
            for ( uint32_t idx = 0; idx < mode1PIDs[pid].formulas.size(); ++idx )
            {
                format.mSignals[idx].mSignalID = pid | ( idx << PID_SIGNAL_BITS_LEFT_SHIFT );
                format.mSignals[idx].mFirstBitPosition = static_cast<uint16_t>(
                    mode1PIDs[pid].formulas[idx].byteOffset * BYTE_SIZE + mode1PIDs[pid].formulas[idx].bitShift );
                format.mSignals[idx].mSizeInBits =
                    static_cast<uint16_t>( ( mode1PIDs[pid].formulas[idx].numOfBytes - 1 ) * BYTE_SIZE +
                                           mode1PIDs[pid].formulas[idx].bitMaskLen );
                format.mSignals[idx].mFactor = mode1PIDs[pid].formulas[idx].scaling;
                format.mSignals[idx].mOffset = mode1PIDs[pid].formulas[idx].offset;
            }
            decoderDictPtr->emplace( pid, format );
        }
    }

    void
    assertSignalValue( const OBDSignal &obdSignal, double expectedSignalValue, SignalType expectedSignalType )
    {
        switch ( expectedSignalType )
        {
        case SignalType::UINT8:
            ASSERT_EQ( static_cast<uint8_t>( obdSignal.signalValue.doubleVal ),
                       static_cast<uint8_t>( expectedSignalValue ) );
            break;
        case SignalType::INT8:
            ASSERT_EQ( static_cast<int8_t>( obdSignal.signalValue.doubleVal ),
                       static_cast<int8_t>( expectedSignalValue ) );
            break;
        case SignalType::UINT16:
            ASSERT_EQ( static_cast<uint16_t>( obdSignal.signalValue.doubleVal ),
                       static_cast<uint16_t>( expectedSignalValue ) );
            break;
        case SignalType::INT16:
            ASSERT_EQ( static_cast<int16_t>( obdSignal.signalValue.doubleVal ),
                       static_cast<int16_t>( expectedSignalValue ) );
            break;
        case SignalType::UINT32:
            ASSERT_EQ( static_cast<uint32_t>( obdSignal.signalValue.doubleVal ),
                       static_cast<uint32_t>( expectedSignalValue ) );
            break;
        case SignalType::INT32:
            ASSERT_EQ( static_cast<int32_t>( obdSignal.signalValue.doubleVal ),
                       static_cast<int32_t>( expectedSignalValue ) );
            break;
        case SignalType::UINT64:
            ASSERT_EQ( obdSignal.signalValue.uint64Val, static_cast<uint64_t>( expectedSignalValue ) );
            break;
        case SignalType::INT64:
            ASSERT_EQ( obdSignal.signalValue.int64Val, static_cast<int64_t>( expectedSignalValue ) );
            break;
        case SignalType::FLOAT:
            ASSERT_FLOAT_EQ( static_cast<float>( obdSignal.signalValue.doubleVal ),
                             static_cast<float>( expectedSignalValue ) );
            break;
        case SignalType::DOUBLE:
            ASSERT_DOUBLE_EQ( obdSignal.signalValue.doubleVal, static_cast<double>( expectedSignalValue ) );
            break;
        case SignalType::BOOLEAN:
            ASSERT_EQ( static_cast<bool>( obdSignal.signalValue.doubleVal ), static_cast<bool>( expectedSignalValue ) );
            break;
        default:
            FAIL() << "Unsupported signal type";
        };
    }
};

class OBDDataDecoderTestWithAllSignalTypes : public OBDDataDecoderTest, public testing::WithParamInterface<SignalType>
{
};

INSTANTIATE_TEST_SUITE_P( AllSignals, OBDDataDecoderTestWithAllSignalTypes, allSignalTypes, signalTypeToString );

class OBDDataDecoderTestWithSignedSignalTypes : public OBDDataDecoderTest,
                                                public testing::WithParamInterface<SignalType>
{
};

INSTANTIATE_TEST_SUITE_P( SignedSignals,
                          OBDDataDecoderTestWithSignedSignalTypes,
                          signedSignalTypes,
                          signalTypeToString );

TEST_P( OBDDataDecoderTestWithAllSignalTypes, FullSingleByte )
{
    SignalType signalType = GetParam();
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 1;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 0;
    signalFormat.mSizeInBits = 8;
    signalFormat.mFactor = (double)100 / 255;
    signalFormat.mOffset = 0;
    signalFormat.mSignalType = signalType;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x99 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    assertSignalValue( info.mPIDsToValues.at( signalFormat.mSignalID ), 60, signalType );
}

TEST_P( OBDDataDecoderTestWithAllSignalTypes, FullSingleByteNegativeOffset )
{
    SignalType signalType = GetParam();
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 1;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 0;
    signalFormat.mSizeInBits = 8;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = -10;
    signalFormat.mSignalType = signalType;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x99 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    assertSignalValue( info.mPIDsToValues.at( signalFormat.mSignalID ), 143, signalType );
}

TEST_P( OBDDataDecoderTestWithAllSignalTypes, FullMultipleBytes )
{
    SignalType signalType = GetParam();
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 2;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 0;
    signalFormat.mSizeInBits = 16;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = 0;
    signalFormat.mSignalType = signalType;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x00, 0x0A };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    assertSignalValue( info.mPIDsToValues.at( signalFormat.mSignalID ), 10, signalType );
}

TEST_P( OBDDataDecoderTestWithAllSignalTypes, PartialByte )
{
    SignalType signalType = GetParam();
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 1;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 2;
    signalFormat.mSizeInBits = 2;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = 0;
    signalFormat.mSignalType = signalType;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    std::vector<uint8_t> txPDUData = { 0x41, pid, 0xFB };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    assertSignalValue( info.mPIDsToValues.at( signalFormat.mSignalID ), 2, signalType );
}

TEST_P( OBDDataDecoderTestWithSignedSignalTypes, FullSingleByteWithUnsignedRawValue )
{
    SignalType signalType = GetParam();
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 1;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 0;
    signalFormat.mSizeInBits = 8;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = 0;
    signalFormat.mSignalType = signalType;
    signalFormat.mIsSigned = false;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    std::vector<uint8_t> txPDUData = { 0x41, pid, 0xC4 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    // Even though the signal type is a signed type, the raw OBD value is not signed.
    // So the raw value 0xC4 should be interpreted as a positive integer instead of negative (-60).
    assertSignalValue( info.mPIDsToValues.at( signalFormat.mSignalID ), 196, signalType );
}

TEST_P( OBDDataDecoderTestWithSignedSignalTypes, FullMultipleBytesWithUnsignedRawValue )
{
    SignalType signalType = GetParam();
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 2;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 0;
    signalFormat.mSizeInBits = 16;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = 0;
    signalFormat.mSignalType = signalType;
    signalFormat.mIsSigned = false;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    std::vector<uint8_t> txPDUData = { 0x41, pid, 0xC4, 0x0A };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    // Even though the signal type is a signed type, the raw OBD value is not signed.
    // So the raw value 0xC40A should be interpreted as a positive integer instead of negative (-15350).
    assertSignalValue( info.mPIDsToValues.at( signalFormat.mSignalID ), 50186, signalType );
}

TEST_P( OBDDataDecoderTestWithSignedSignalTypes, PartialByteWithUnsignedRawValue )
{
    SignalType signalType = GetParam();
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 1;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 2;
    signalFormat.mSizeInBits = 5;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = 0;
    signalFormat.mSignalType = signalType;
    signalFormat.mIsSigned = false;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    // 0x4C shifted 2 bits to the right (without extending the sign) = 0x13 = 19
    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x4C };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    assertSignalValue( info.mPIDsToValues.at( signalFormat.mSignalID ), 19, signalType );
}

TEST_F( OBDDataDecoderTest, KeepPrecisionForUInt64 )
{
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 8;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 0;
    signalFormat.mSizeInBits = 64;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = -100000;
    signalFormat.mSignalType = SignalType::UINT64;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    // 2305843009213693951, which when represented as a double loses precision
    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x1F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    // Ensure that we are not casting to double anywhere
    ASSERT_EQ( info.mPIDsToValues.at( signalFormat.mSignalID ).signalValue.uint64Val, 2305843009213593951UL );
}

TEST_F( OBDDataDecoderTest, KeepPrecisionForInt64 )
{
    PID pid = 0xEF;
    CANMessageFormat format;
    format.mMessageID = pid;
    format.mSizeInBytes = 8;
    CANSignalFormat signalFormat;
    signalFormat.mSignalID = 0x100000EF;
    signalFormat.mFirstBitPosition = 0;
    signalFormat.mSizeInBits = 64;
    signalFormat.mFactor = 1.0;
    signalFormat.mOffset = -100000;
    signalFormat.mSignalType = SignalType::INT64;
    format.mSignals.emplace_back( signalFormat );
    decoderDictPtr->emplace( pid, format );

    // -6917529027641081857, which when represented as a double loses precision
    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x9F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    // Ensure that we are not casting to double anywhere
    ASSERT_EQ( info.mPIDsToValues.at( signalFormat.mSignalID ).signalValue.int64Val, -6917529027641181857L );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedSupportedPIDs )
{
    // supported PID: 0x01 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
    // 0x10, 0x11, 0x13, 0x15, 0x19, 0x1C, 0x21
    std::vector<uint8_t> txPDUData = { 0x41, 0x00, 0xBF, 0xBF, 0xA8, 0x91, 0x20, 0x80, 0x00, 0x00, 0x00 };
    SupportedPIDs pids;

    ASSERT_TRUE( decoder.decodeSupportedPIDs( SID::CURRENT_STATS, txPDUData, pids ) );
    // Expect only what we support in the SW
    // The PID list should NOT include the Supported PID ID that was requested i.e. 0X00
    std::vector<PID> expectedSupportedPIDs = { 0x01, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0B, 0x0C,
                                               0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x13, 0x15, 0x19, 0x1C, 0x21 };
    ASSERT_EQ( pids.size(), expectedSupportedPIDs.size() );
    for ( size_t idx = 0; idx < expectedSupportedPIDs.size(); ++idx )
    {
        ASSERT_EQ( pids[idx], expectedSupportedPIDs[idx] );
    }
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedEngineLoad )
{
    // Engine load of 60 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x04, 0x99 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x04 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );

    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_LOAD ) ).signalValue.doubleVal, 60 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedEngineTemperature )
{
    // Engine load of 70 deg
    std::vector<uint8_t> txPDUData = { 0x41, 0x05, 0x6E };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x05 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_COOLANT_TEMPERATURE ) ).signalValue.doubleVal, 70 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedFuelTrim )
{
    // Fuel Trim on all banks of value 50
    // Raw Value is 192 -100 * 1.28
    std::vector<uint8_t> txPDUData = { 0x41, 0x06, 0xC0, 0x07, 0xC0, 0x08, 0xC0, 0x09, 0xC0 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x06, 0x07, 0x08, 0x09 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::SHORT_TERM_FUEL_TRIM_BANK_1 ) ).signalValue.doubleVal, 50 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::SHORT_TERM_FUEL_TRIM_BANK_2 ) ).signalValue.doubleVal, 50 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::LONG_TERM_FUEL_TRIM_BANK_1 ) ).signalValue.doubleVal, 50 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::LONG_TERM_FUEL_TRIM_BANK_2 ) ).signalValue.doubleVal, 50 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedIntakeManifoldPressure )
{
    // IntakeManifoldPressure of 200 pka
    std::vector<uint8_t> txPDUData = { 0x41, 0x0B, 0xC8 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x0B }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::INTAKE_MANIFOLD_ABSOLUTE_PRESSURE ) ).signalValue.doubleVal,
        200 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedIntakeAirFLowTemperature )
{
    // IntakeAirFLowTemperature of 30 deg
    std::vector<uint8_t> txPDUData = { 0x41, 0x0F, 0x46 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x0F }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::INTAKE_AIR_FLOW_TEMPERATURE ) ).signalValue.doubleVal, 30 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedMAFRate )
{
    // MAFRate od 25 grams/sec
    std::vector<uint8_t> txPDUData = { 0x41, 0x10, 0x0A, 0x0A };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x10 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::MAF_RATE ) ).signalValue.doubleVal,
                      ( 256.0 * 0x0A + 0x0A ) / 100 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedThrottlePosition )
{
    // ThrottlePosition of 50 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x11, 0x80 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x11 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::THROTTLE_POSITION ) ).signalValue.doubleVal,
                      (double)0x80 * 100 / 255 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedOxygenSensorX_1 )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0x14, 0x10, 0x20 };
    EmissionInfo info;

    for ( PID pid = toUType( EmissionPIDs::OXYGEN_SENSOR1_1 ); pid <= toUType( EmissionPIDs::OXYGEN_SENSOR8_1 ); ++pid )
    {
        txPDUData[1] = pid;
        ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
        ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
        ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( pid | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) ).signalValue.doubleVal,
                          (double)0x10 / 200 );
        ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( pid | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) ).signalValue.doubleVal,
                          (double)0x20 * 100 / 128 - 100 );
    }
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedRuntimeSinceEngineStart )
{
    // RuntimeSinceEngineStart of 500 seconds
    std::vector<uint8_t> txPDUData = { 0x41, 0x1F, 0x01, 0xF4 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x1F }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::RUNTIME_SINCE_ENGINE_START ) ).signalValue.doubleVal, 500 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedDistanceTraveledWithMIL )
{
    // DistanceTraveledWithMIL of 10 km
    std::vector<uint8_t> txPDUData = { 0x41, 0x21, 0x00, 0x0A };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x21 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::DISTANCE_TRAVELED_WITH_MIL ) ).signalValue.doubleVal, 10 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedOxygenSensorX_2 )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0x24, 0x10, 0x20, 0x30, 0x40 };
    EmissionInfo info;

    for ( PID pid = toUType( EmissionPIDs::OXYGEN_SENSOR1_2 ); pid <= toUType( EmissionPIDs::OXYGEN_SENSOR8_2 ); ++pid )
    {
        txPDUData[1] = pid;
        ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
        ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
        ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( pid | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) ).signalValue.doubleVal,
                          ( 256 * 0x10 + 0x20 ) * 0.0000305 );
        ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( pid | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) ).signalValue.doubleVal,
                          ( 256 * 0x30 + 0x40 ) * 0.000122 );
    }
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedFuelTankLevel )
{
    // FuelTankLevel of 100 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x2F, 0xFF };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x2F }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::FUEL_TANK_LEVEL ) ).signalValue.doubleVal, 100 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedDistanceTraveledSinceClearedDTC )
{
    // DistanceTraveledSinceClearedDTC of 10 km
    std::vector<uint8_t> txPDUData = { 0x41, 0x31, 0x00, 0x0A };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x31 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::DISTANCE_TRAVELED_SINCE_CLEARED_DTC ) ).signalValue.doubleVal,
        10 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedControlModuleVoltage )
{
    // ControlModuleVoltage of 25V
    std::vector<uint8_t> txPDUData = { 0x41, 0x42, 0x64, 0x64 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x42 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::CONTROL_MODULE_VOLTAGE ) ).signalValue.doubleVal,
                      ( 256.0 * 100 + 100 ) / 1000 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedRelativeThrottlePosition )
{
    // RelativeThrottlePosition of 50 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x45, 0x80 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x45 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::RELATIVE_THROTTLE_POSITION ) ).signalValue.doubleVal,
        (double)0x80 * 100 / 255 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedAmbientAireTemperature )
{
    // AmbientAireTemperature of 30 deg
    std::vector<uint8_t> txPDUData = { 0x41, 0x46, 0x46 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x46 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::AMBIENT_AIR_TEMPERATURE ) ).signalValue.doubleVal,
                      30 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedRelativePedalPosition )
{
    // RelativePedalPosition of 100 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x5A, 0xFF };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x5A }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::RELATIVE_ACCELERATOR_PEDAL_POSITION ) ).signalValue.doubleVal,
        100 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedBatteryRemainingLife )
{
    // BatteryRemainingLife of 100 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x5B, 0xFF };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x5B }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::HYBRID_BATTERY_PACK_REMAINING_LIFE ) ).signalValue.doubleVal,
        100 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedEngineOilTemperature )
{
    // EngineOilTemperature of  30 deg
    std::vector<uint8_t> txPDUData = { 0x41, 0x5C, 0x46 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x5C }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_OIL_TEMPERATURE ) ).signalValue.doubleVal,
                      30 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedDriverDemandTorque )
{
    // DriverDemandTorque of 100 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x61, 0xE1 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x61 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::DRIVER_DEMAND_PERCENT_TORQUE ) ).signalValue.doubleVal, 100 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedActualEngineTorque )
{
    // ActualEngineTorque of 100 %
    std::vector<uint8_t> txPDUData = { 0x41, 0x62, 0xE1 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x62 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ACTUAL_PERCENT_TORQUE ) ).signalValue.doubleVal,
                      100 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedReferenceEngineTorque )
{
    // ReferenceEngineTorque of 25700 Nm
    std::vector<uint8_t> txPDUData = { 0x41, 0x63, 0x64, 0x64 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x63 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_REFERENCE_PERCENT_TORQUE ) ).signalValue.doubleVal,
        25700 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedBoostPressureControl )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0x70, 0x3F, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x0F };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x70 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x3F );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 2 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 3 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 4 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 5 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 6 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 7 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x00 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedVariableGeometryTurboControl )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0x71, 0x3F, 0x10, 0x20, 0x30, 0x40, 0x0F };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x71 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x3F );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x10 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 2 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x20 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 3 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x30 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 4 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x40 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 5 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 6 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 7 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x00 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedBoostPressureControlAndVariableGeometryTurboControl )
{
    std::vector<uint8_t> txPDUData = { 0x41,
                                       0x70,
                                       0x3F,
                                       0x64,
                                       0x64,
                                       0x64,
                                       0x64,
                                       0x64,
                                       0x64,
                                       0x64,
                                       0x64,
                                       0x0F,
                                       0x71,
                                       0x3F,
                                       0x10,
                                       0x20,
                                       0x30,
                                       0x40,
                                       0x0F };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x70, 0x71 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x3F );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 2 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 3 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 4 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        803.125 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 5 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 6 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::BOOST_PRESSURE_CONTROL ) | ( 7 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x00 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x3F );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x10 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 2 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x20 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 3 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x30 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 4 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        (double)100 / 255 * 0x40 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 5 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 6 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x03 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::VARIABLE_GEOMETRY_TURBO_CONTROL ) | ( 7 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x00 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedEngineRunTime )
{
    std::vector<uint8_t> txPDUData = {
        0x41, 0x7F, 0x08, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x7F }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_RUN_TIME ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x08 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_RUN_TIME ) | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        16909060 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_RUN_TIME ) | ( 2 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        16909060 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_RUN_TIME ) | ( 3 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        16909060 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderExhaustGasTemperatureSensor )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0x98, 0xFF, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x98 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::EXHAUST_GAS_TEMPERATURE_SENSORA ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0xFF );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::EXHAUST_GAS_TEMPERATURE_SENSORA ) | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x1020 * 0.1 - 40 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::EXHAUST_GAS_TEMPERATURE_SENSORA ) | ( 2 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x3040 * 0.1 - 40 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::EXHAUST_GAS_TEMPERATURE_SENSORA ) | ( 3 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x5060 * 0.1 - 40 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues
            .at( toUType( EmissionPIDs::EXHAUST_GAS_TEMPERATURE_SENSORA ) | ( 4 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x7080 * 0.1 - 40 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedTransmissionActualGear )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0xA4, 0xFF, 0xF0, 0xAA, 0x55 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0xA4 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::TRANSMISSION_ACTUAL_GEAR ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0xFF );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::TRANSMISSION_ACTUAL_GEAR ) | ( 1 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0x0F );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::TRANSMISSION_ACTUAL_GEAR ) | ( 2 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
            .signalValue.doubleVal,
        0xAA55 * 0.001 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedOdometer )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0xA6, 0x01, 0x10, 0xAA, 0x55 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0xA6 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ODOMETER ) | ( 0 << PID_SIGNAL_BITS_LEFT_SHIFT ) )
                          .signalValue.doubleVal,
                      0x0110AA55 * 0.1 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedFuelPressure )
{
    // Engine load of 450 pka
    std::vector<uint8_t> txPDUData = { 0x41, 0x0A, 0x96 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x0A }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::FUEL_PRESSURE ) ).signalValue.doubleVal, 450 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedEngineSpeed )
{
    // Engine load of 666 rpm
    std::vector<uint8_t> txPDUData = { 0x41, 0x0C, 0x0A, 0x6B };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x0C }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_SPEED ) ).signalValue.doubleVal,
                      ( 256.0 * 0x0A + 0x6B ) / 4 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedVehicleSpeed )
{
    // Engine load of 35 kph
    std::vector<uint8_t> txPDUData = { 0x41, 0x0D, 0x23 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x0D }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::VEHICLE_SPEED ) ).signalValue.doubleVal, 35 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedMultiplePIDs )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0x04, 0x99, 0x05, 0x6E, 0x0A, 0x96, 0x0C, 0x0A, 0x6B, 0x0D, 0x23 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x04, 0x05, 0x0A, 0x0C, 0x0D }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_LOAD ) ).signalValue.doubleVal, 60 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_COOLANT_TEMPERATURE ) ).signalValue.doubleVal, 70 );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::FUEL_PRESSURE ) ).signalValue.doubleVal, 450 );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_SPEED ) ).signalValue.doubleVal,
                      ( 256.0 * 0x0A + 0x6B ) / 4 );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::VEHICLE_SPEED ) ).signalValue.doubleVal, 35 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedMultiplePIDsWhereResponseOrderDifferentThanRequestOrder )
{
    std::vector<uint8_t> txPDUData = { 0x41, 0x04, 0x99, 0x05, 0x6E, 0x0A, 0x96, 0x0C, 0x0A, 0x6B, 0x0D, 0x23 };
    EmissionInfo info;

    ASSERT_TRUE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x0D, 0x04, 0x0A, 0x0C, 0x05 }, txPDUData, info ) );
    ASSERT_EQ( info.mSID, SID::CURRENT_STATS );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_LOAD ) ).signalValue.doubleVal, 60 );
    ASSERT_DOUBLE_EQ(
        info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_COOLANT_TEMPERATURE ) ).signalValue.doubleVal, 70 );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::FUEL_PRESSURE ) ).signalValue.doubleVal, 450 );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::ENGINE_SPEED ) ).signalValue.doubleVal,
                      ( 256.0 * 0x0A + 0x6B ) / 4 );
    ASSERT_DOUBLE_EQ( info.mPIDsToValues.at( toUType( EmissionPIDs::VEHICLE_SPEED ) ).signalValue.doubleVal, 35 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedMultiplePIDsWrongPDU )
{

    std::vector<uint8_t> txPDUData = { 0x41, 0xAA, 0x99, 0xBB, 0xBB, 0xCC, 0xEE, 0xCC, 0xFF };
    EmissionInfo info;

    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0xAA, 0xCC, 0xFF }, txPDUData, info ) );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedMultiplePIDsNegativeResponse )
{

    std::vector<uint8_t> txPDUData = { 0x78, 0x09, 0x99 };
    EmissionInfo info;
    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 0x09 }, txPDUData, info ) );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderExtractDTCStringTest )
{

    std::vector<uint8_t> txPDUData = { 0x01, 0x96 };
    std::string dtc;

    ASSERT_TRUE( decoder.extractDTCString( txPDUData[0], txPDUData[1], dtc ) );
    ASSERT_EQ( dtc, "P0196" );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderdecodeDTCsTest )
{
    // 4 DTC reported on each domain
    std::vector<uint8_t> txPDUData = { 0x43, 0x04, 0x01, 0x43, 0x41, 0x96, 0x81, 0x48, 0xC1, 0x48 };
    DTCInfo info;

    ASSERT_TRUE( decoder.decodeDTCs( SID::STORED_DTC, txPDUData, info ) );
    ASSERT_EQ( info.mDTCCodes.size(), 4 );
    ASSERT_EQ( info.mDTCCodes[0], "P0143" );
    ASSERT_EQ( info.mDTCCodes[1], "C0196" );
    ASSERT_EQ( info.mDTCCodes[2], "B0148" );
    ASSERT_EQ( info.mDTCCodes[3], "U0148" );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderdecodeDTCsCorruptDataTest )
{
    // Wrong count of DTCs
    std::vector<uint8_t> txPDUData = { 0x43, 0x04, 0x01, 0x43 };
    DTCInfo info;

    ASSERT_FALSE( decoder.decodeDTCs( SID::STORED_DTC, txPDUData, info ) );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderdecodeVIN )
{
    // Wrong count of DTCs
    std::vector<uint8_t> txPDUData = { 0x49, 0x02, 0x01, 0x31, 0x47, 0x31, 0x4A, 0x43, 0x35, 0x34,
                                       0x34, 0x34, 0x52, 0x37, 0x32, 0x35, 0x32, 0x33, 0x36, 0x37 };
    std::string vin;

    ASSERT_TRUE( decoder.decodeVIN( txPDUData, vin ) );
    ASSERT_EQ( vin, "1G1JC5444R7252367" );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderNoVIN )
{
    // Wrong count of DTCs
    std::vector<uint8_t> txPDUData = { 0x49, 0x02 };
    std::string vin;

    ASSERT_FALSE( decoder.decodeVIN( txPDUData, vin ) );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderCorruptFormulaTest )
{
    PID pid = 0x66;
    const auto &originalFormula = decoderDictPtr->at( pid ).mSignals[0];
    // corrupt mFirstBitPosition to out of bound
    decoderDictPtr->at( pid ).mSignals[0].mFirstBitPosition = 80;
    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x99 };
    EmissionInfo info;

    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
    decoderDictPtr->at( pid ).mSignals[0] = originalFormula;
    // corrupt mSizeInBits to out of bound
    decoderDictPtr->at( pid ).mSignals[0].mSizeInBits = 80;
    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );

    decoderDictPtr->at( pid ).mSignals[0] = originalFormula;
    // corrupt mSizeInBits to be invalid
    decoderDictPtr->at( pid ).mSignals[0].mSizeInBits = 33;
    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );

    decoderDictPtr->at( pid ).mSignals[0] = originalFormula;
    // corrupt mFirstBitPosition to be invalid. Because mSizeInBit is 8, First Bit position
    // has to be aligned with byte
    decoderDictPtr->at( pid ).mSignals[0].mFirstBitPosition = 2;
    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
}

// In this test, we aim to test OBDDataDecoder with ECU response mismatch with decoder manifest.
// We shall expect decodeEmissionPIDs not decode this invalid ECU response.
TEST_F( OBDDataDecoderTest, OBDDataDecoderWithECUResponseMismatchWithDecoderManifest )
{
    // Below ECU response contains PID 107 which has mismatched response than decoder manifest
    std::vector<uint8_t> txPDUData = { 0x41, 107, 0, 108, 0, 109, 0, 13, 102, 12, 252, 110, 0, 111, 0, 112, 0 };
    EmissionInfo info;
    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { 107, 108, 109, 110, 111, 112 }, txPDUData, info ) );
    ASSERT_EQ( info.mPIDsToValues.size(), 0 );
}

TEST_F( OBDDataDecoderTest, OBDDataDecoderDecodedEngineLoadCorruptDecoderDictionaryTest )
{
    PID pid = 0x04;
    // corrupt decoder dictionary pointer to nullptr
    decoderDictPtr = nullptr;
    std::vector<uint8_t> txPDUData = { 0x41, pid, 0x99 };
    EmissionInfo info;

    ASSERT_FALSE( decoder.decodeEmissionPIDs( SID::CURRENT_STATS, { pid }, txPDUData, info ) );
}