//--------------------------------------------------------------------------
// Copyright (C) 2024-2025 Cisco and/or its affiliates. All rights reserved.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License Version 2 as published
// by the Free Software Foundation.  You may not use, modify or distribute
// this program under any other version of the GNU General Public License.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//--------------------------------------------------------------------------
// iec104_parse_apdu_test.cc author Yehor Furman <yefurman@cisco.com>

#include "../iec104_decode.cc"

#include <CppUTest/CommandLineTestRunner.h>
#include <CppUTest/TestHarness.h>
#include <CppUTestExt/MockSupport.h>

THREAD_LOCAL const snort::Trace* iec104_trace = nullptr;

namespace snort
{
    uint8_t TraceApi::get_constraints_generation() { return 0; }
    void TraceApi::filter(const Packet&) { }
    void trace_vprintf(const char*, uint8_t, const char*, const Packet*, const char*, va_list) { }

    Packet::Packet(bool) { memset(this, 0, sizeof(*this)); }
    Packet::~Packet() = default;

    FlowData::FlowData(unsigned u, Inspector*) : id(u), handler(nullptr) { }
    FlowData::~FlowData() = default;
}

unsigned Iec104FlowData::inspector_id = 0;
Iec104FlowData::Iec104FlowData() : snort::FlowData(inspector_id) { }
Iec104FlowData::~Iec104FlowData() = default;

void parseIec104ApciU(const Iec104ApciU*) { }
void parseIec104ApciS(const Iec104ApciS*) { }
void parseIec104ApciI(const Iec104ApciI*, const uint16_t&) { }

TEST_GROUP(iec104_decode_type_i_min_len)
{
    // cppcheck-suppress constVariablePointer
    snort::Packet* packet = nullptr;
    // cppcheck-suppress constVariablePointer
    Iec104FlowData* flow_data = nullptr;

    void setup() override
    {
        // cppcheck-suppress unreadVariable
        packet = new snort::Packet(false);
        // cppcheck-suppress unreadVariable
        flow_data = new Iec104FlowData();
    }

    void teardown() override
    {
        delete packet;
        delete flow_data;
    }
};

TEST(iec104_decode_type_i_min_len, type_i_6_bytes_rejected)
{
    const uint8_t type_i_6_bytes[] = {0x68, 0x04, 0x00, 0x00, 0x00, 0x00};
    snort::Packet* const local_packet = packet;
    Iec104FlowData* const local_flow_data = flow_data;
    local_packet->data = type_i_6_bytes;
    local_packet->dsize = sizeof(type_i_6_bytes);
    bool result = Iec104Decode(local_packet, local_flow_data);
    CHECK_FALSE(result);
}

TEST(iec104_decode_type_i_min_len, type_i_11_bytes_rejected)
{
    const uint8_t type_i_11_bytes[] = {0x68, 0x09, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x01, 0x00};
    snort::Packet* const local_packet = packet;
    Iec104FlowData* const local_flow_data = flow_data;
    local_packet->data = type_i_11_bytes;
    local_packet->dsize = sizeof(type_i_11_bytes);
    bool result = Iec104Decode(local_packet, local_flow_data);
    CHECK_FALSE(result);
}

TEST(iec104_decode_type_i_min_len, type_i_12_bytes_accepted)
{
    const uint8_t type_i_12_bytes[] = {0x68, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00};
    snort::Packet* const local_packet = packet;
    Iec104FlowData* const local_flow_data = flow_data;
    local_packet->data = type_i_12_bytes;
    local_packet->dsize = sizeof(type_i_12_bytes);
    bool result = Iec104Decode(local_packet, local_flow_data);
    CHECK_TRUE(result);
    CHECK_EQUAL(IEC104_APCI_TYPE_I, local_flow_data->ssn_data.iec104_apci_type);
}

TEST(iec104_decode_type_i_min_len, type_s_6_bytes_accepted)
{
    const uint8_t type_s_6_bytes[] = {0x68, 0x04, 0x01, 0x00, 0x00, 0x00};
    snort::Packet* const local_packet = packet;
    Iec104FlowData* const local_flow_data = flow_data;
    local_packet->data = type_s_6_bytes;
    local_packet->dsize = sizeof(type_s_6_bytes);
    bool result = Iec104Decode(local_packet, local_flow_data);
    CHECK_TRUE(result);
    CHECK_EQUAL(IEC104_APCI_TYPE_S, local_flow_data->ssn_data.iec104_apci_type);
}

TEST(iec104_decode_type_i_min_len, type_u_6_bytes_accepted)
{
    const uint8_t type_u_6_bytes[] = {0x68, 0x04, 0x03, 0x00, 0x00, 0x00};
    snort::Packet* const local_packet = packet;
    Iec104FlowData* const local_flow_data = flow_data;
    local_packet->data = type_u_6_bytes;
    local_packet->dsize = sizeof(type_u_6_bytes);
    bool result = Iec104Decode(local_packet, local_flow_data);
    CHECK_TRUE(result);
    CHECK_EQUAL(IEC104_APCI_TYPE_U, local_flow_data->ssn_data.iec104_apci_type);
}

int main(int argc, char** argv)
{
    return CommandLineTestRunner::RunAllTests(argc, argv);
}
