/*
**
** Copyright 2017, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
**     http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/

#include <android/hardware/confirmationui/support/cbor.h>

#include <cstddef>
#include <cstdint>
#include <iomanip>
#include <iostream>

#include <gtest/gtest.h>

using namespace android::hardware::confirmationui::support;

uint8_t testVector[] = {
    0xA4, 0x63, 0x6B, 0x65, 0x79, 0x65, 0x76, 0x61, 0x6C, 0x75, 0x65, 0x63, 0x6B, 0x65, 0x79, 0x4D,
    0x31, 0x30, 0x30, 0x31, 0x30, 0x31, 0x30, 0x31, 0x30, 0x30, 0x31, 0x30, 0x00, 0x04, 0x07, 0x1B,
    0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x07, 0x1A, 0xFD, 0x49, 0x8C, 0xFF,
    0xFF, 0x82, 0x69, 0xE2, 0x99, 0xA8, 0xE2, 0x9A, 0x96, 0xE2, 0xB6, 0x96, 0x59, 0x01, 0x91, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61,
    0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x00,
};

// 400 'a's and a '\0'
constexpr char fourHundredAs[] =
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    "aaaaaaaaaaaaaaaaaaaaaaaa";

WriteState writeTest(WriteState state) {
    return write(state,                                                     //
                 map(                                                       //
                     pair(text("key"), text("value")),                      //
                     pair(text("key"), bytes("100101010010")),              //
                     pair(4, 7),                                            //
                     pair((UINT64_C(1) << 62), INT64_C(-2000000000000000))  //
                     ),                                                     //
                 arr(text("♨⚖ⶖ"), bytes(fourHundredAs)));
}

TEST(Cbor, FeatureTest) {
    uint8_t buffer[0x1000];
    WriteState state(buffer);
    state = writeTest(state);
    ASSERT_EQ(sizeof(testVector), size_t(state.data_ - buffer));
    ASSERT_EQ(Error::OK, state.error_);
    ASSERT_EQ(0, memcmp(buffer, testVector, sizeof(testVector)));
}

// Test if in all write cases an out of data error is correctly propagated and we don't
// write beyond  the end of the buffer.
TEST(Cbor, BufferTooShort) {
    uint8_t buffer[0x1000];
    for (size_t s = 1; s < sizeof(testVector); ++s) {
        memset(buffer, 0x22, 0x1000);  // 0x22 is not in the testVector
        WriteState state(buffer, s);
        state = writeTest(state);
        for (size_t t = s; t < 0x1000; ++t) {
            ASSERT_EQ(0x22, buffer[t]);  // check if a canary has been killed
        }
        ASSERT_EQ(Error::OUT_OF_DATA, state.error_);
    }
}

TEST(Cbor, MalformedUTF8Test_Stray) {
    uint8_t buffer[20];
    WriteState state(buffer);
    char malformed[] = {char(0x80), 0};
    state = write(state, text(malformed));
    ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}

TEST(Cbor, MalformendUTF8Test_StringEndsMidMultiByte) {
    uint8_t buffer[20];
    WriteState state(buffer);
    char malformed[] = {char(0xc0), 0};
    state = write(state, text(malformed));
    ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}

TEST(Cbor, UTF8Test_TwoBytes) {
    uint8_t buffer[20];
    WriteState state(buffer);
    char neat[] = {char(0xc3), char(0x82), 0};
    state = write(state, text(neat));
    ASSERT_EQ(Error::OK, state.error_);
}

TEST(Cbor, UTF8Test_ThreeBytes) {
    uint8_t buffer[20];
    WriteState state(buffer);
    char neat[] = {char(0xe3), char(0x82), char(0x82), 0};
    state = write(state, text(neat));
    ASSERT_EQ(Error::OK, state.error_);
}

TEST(Cbor, UTF8Test_FourBytes) {
    uint8_t buffer[20];
    WriteState state(buffer);
    char neat[] = {char(0xf3), char(0x82), char(0x82), char(0x82), 0};
    state = write(state, text(neat));
    ASSERT_EQ(Error::OK, state.error_);
}

TEST(Cbor, MalformendUTF8Test_CharacterTooLong) {
    uint8_t buffer[20];
    WriteState state(buffer);
    char malformed[] = {char(0xf8), char(0x82), char(0x82), char(0x82), char(0x82), 0};
    state = write(state, text(malformed));
    ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}

TEST(Cbor, MalformendUTF8Test_StringEndsMidMultiByte2) {
    uint8_t buffer[20];
    WriteState state(buffer);
    char malformed[] = {char(0xc0), char(0x82), char(0x83), 0};
    state = write(state, text(malformed));
    ASSERT_EQ(Error::MALFORMED_UTF8, state.error_);
}

TEST(Cbor, MinimalViableHeaderSizeTest) {
    uint8_t buffer[20];
    WriteState state(buffer);
    state = writeHeader(state, Type::NUMBER, 23);
    ASSERT_EQ(state.data_ - buffer, 1);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 24);
    ASSERT_EQ(state.data_ - buffer, 2);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 0xff);
    ASSERT_EQ(state.data_ - buffer, 2);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 0x100);
    ASSERT_EQ(state.data_ - buffer, 3);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 0xffff);
    ASSERT_EQ(state.data_ - buffer, 3);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 0x10000);
    ASSERT_EQ(state.data_ - buffer, 5);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 0xffffffff);
    ASSERT_EQ(state.data_ - buffer, 5);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 0x100000000);
    ASSERT_EQ(state.data_ - buffer, 9);

    state = WriteState(buffer);
    state = writeHeader(state, Type::NUMBER, 0xffffffffffffffff);
    ASSERT_EQ(state.data_ - buffer, 9);
}
