/*
 * Copyright (C) 2016, 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 <array>

#include "gtest/gtest.h"

#include "wifilogd/byte_buffer.h"
#include "wifilogd/local_utils.h"
#include "wifilogd/memory_reader.h"

namespace android {
namespace wifilogd {

using local_utils::GetMaxVal;

TEST(MemoryReaderTest, BoolConversionNonEmptyBufferYieldsTrue) {
  constexpr std::array<uint8_t, 1> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_TRUE(memory_reader);
}

TEST(MemoryReaderTest, BoolConversionEmptyBufferYieldsFalse) {
  constexpr std::array<uint8_t, 0> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_FALSE(memory_reader);
}

TEST(MemoryReaderTest, BoolConversionNullBufferYieldsFalse) {
  MemoryReader memory_reader(nullptr, 10);
  EXPECT_FALSE(memory_reader);
}

TEST(MemoryReaderTest, CopyAssignmentWorks) {
  constexpr std::array<uint8_t, 3> buffer{{1, 2, 3}};

  MemoryReader reader1{buffer.data(), buffer.size()};
  EXPECT_EQ(buffer[0], *reader1.GetBytesOrDie(sizeof(uint8_t)));

  // A copy of reader1 should reflect the byte already read.
  MemoryReader reader2 = reader1;
  EXPECT_EQ(buffer[1], *reader2.GetBytesOrDie(sizeof(uint8_t)));

  // reader1 should _not_ reflect the additional read on reader2.
  EXPECT_GT(reader1.size(), reader2.size());
}

TEST(MemoryReaderTest, CopyConstructionWorks) {
  constexpr std::array<uint8_t, 3> buffer{{1, 2, 3}};

  MemoryReader reader1{buffer.data(), buffer.size()};
  EXPECT_EQ(buffer[0], *reader1.GetBytesOrDie(sizeof(uint8_t)));

  // A copy of reader1 should reflect the byte already read.
  MemoryReader reader2(reader1);
  EXPECT_EQ(buffer[1], *reader2.GetBytesOrDie(sizeof(uint8_t)));

  // reader1 should _not_ reflect the additional read on reader2.
  EXPECT_GT(reader1.size(), reader2.size());
}

TEST(MemoryReaderTest, CopyOutOrDieCopiesData) {
  constexpr struct Message {
    int a;
    char b;
  } original_message{5, 'c'};
  const auto& duplicate_message =
      MemoryReader(&original_message, sizeof(original_message))
          .CopyOutOrDie<Message>();
  EXPECT_EQ(original_message.a, duplicate_message.a);
  EXPECT_EQ(original_message.b, duplicate_message.b);
}

TEST(MemoryReaderTest, CopyOutOrDieWorksForMultipleObjects) {
  constexpr struct M1 {
    int a;
    char b;
  } first_message{1, 'a'};
  constexpr struct M2 {
    uint64_t a;
    uint64_t b;
  } second_message{0, GetMaxVal(second_message.b)};
  const auto& buf = ByteBuffer<sizeof(first_message) + sizeof(second_message)>()
                        .AppendOrDie(&first_message, sizeof(first_message))
                        .AppendOrDie(&second_message, sizeof(second_message));

  MemoryReader memory_reader(buf.data(), buf.size());
  const auto& copy_of_first_message = memory_reader.CopyOutOrDie<M1>();
  const auto& copy_of_second_message = memory_reader.CopyOutOrDie<M2>();
  EXPECT_EQ(first_message.a, copy_of_first_message.a);
  EXPECT_EQ(first_message.b, copy_of_first_message.b);
  EXPECT_EQ(second_message.a, copy_of_second_message.a);
  EXPECT_EQ(second_message.b, copy_of_second_message.b);
}

TEST(MemoryReaderTest, GetBytesOrDieSucceedsOnSmallRead) {
  constexpr std::array<uint8_t, 1024> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_EQ(buffer.data(), memory_reader.GetBytesOrDie(1));
}

TEST(MemoryReaderTest, GetBytesOrDieSucceedsOnFullRead) {
  constexpr std::array<uint8_t, 1024> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_EQ(buffer.data(), memory_reader.GetBytesOrDie(buffer.size()));
}

TEST(MemoryReaderTest, GetBytesOrDieCanConsumeFullBufferInParts) {
  constexpr std::array<uint8_t, 1024> buffer{};
  static_assert(buffer.size() % 2 == 0, "Test will not consume whole buffer");
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_EQ(buffer.data(), memory_reader.GetBytesOrDie(buffer.size() / 2));
  EXPECT_EQ(buffer.data() + buffer.size() / 2,
            memory_reader.GetBytesOrDie(buffer.size() / 2));
}

TEST(MemoryReaderTest, SizeIsCorrectForNonEmptyBuffer) {
  constexpr std::array<uint8_t, 1> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_EQ(buffer.size(), memory_reader.size());
}

TEST(MemoryReaderTest, SizeIsCorrectForEmptyBuffer) {
  constexpr std::array<uint8_t, 0> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_EQ(buffer.size(), memory_reader.size());
}

TEST(MemoryReaderTest, SizeIsCorrectAfterSmallRead) {
  constexpr std::array<uint8_t, 1024> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  memory_reader.GetBytesOrDie(1);
  EXPECT_EQ(buffer.size() - 1, memory_reader.size());
}

TEST(MemoryReaderTest, SizeIsCorrectAfterFullRead) {
  constexpr std::array<uint8_t, 1024> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  memory_reader.GetBytesOrDie(buffer.size());
  EXPECT_EQ(0U, memory_reader.size());
}

TEST(MemoryReaderTest, SizeIsZeroForNullBuffer) {
  MemoryReader memory_reader(nullptr, 0);
  EXPECT_EQ(0U, memory_reader.size());
}

// Per
// github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-tests,
// death tests should be specially named.

TEST(MemoryReaderDeathTest, CopyOutOrDieWithNullBufferCausesDeath) {
  constexpr struct Message {
    int a;
    char b;
  } original{5, 'c'};
  EXPECT_DEATH(MemoryReader(nullptr, sizeof(original)).CopyOutOrDie<Message>(),
               "Check failed");
}

TEST(MemoryReaderDeathTest, CopyOutOrDieWithShortBufferCausesDeath) {
  constexpr struct Message {
    int a;
    char b;
  } original{5, 'c'};
  EXPECT_DEATH(
      MemoryReader(&original, sizeof(original) - 1).CopyOutOrDie<Message>(),
      "Check failed");
}

TEST(MemoryReaderDeathTest, CopyOutOrDieOverrunWithMultipleReadsCausesDeath) {
  constexpr struct Message {
    int a;
    char b;
  } message{5, 'c'};
  MemoryReader memory_reader(&message, sizeof(message));
  memory_reader.CopyOutOrDie<Message>();
  EXPECT_DEATH(memory_reader.CopyOutOrDie<Message>(), "Check failed");
}

TEST(MemoryReaderTest, GetBytesOrDieWithNullBufferCausesDeath) {
  constexpr std::array<uint8_t, 1024> buffer{};
  MemoryReader memory_reader(nullptr, buffer.size());
  EXPECT_DEATH(memory_reader.GetBytesOrDie(buffer.size()), "Check failed");
}

TEST(MemoryReaderTest, GetBytesOrDieWithShortBufferCausesDeath) {
  constexpr std::array<uint8_t, 1024> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_DEATH(memory_reader.GetBytesOrDie(buffer.size() + 1), "Check failed");
}

TEST(MemoryReaderTest, GetBytesOrDieOverrunWithMultipleReadsCausesDeath) {
  constexpr std::array<uint8_t, 1024> buffer{};
  MemoryReader memory_reader(buffer.data(), buffer.size());
  EXPECT_EQ(buffer.data(), memory_reader.GetBytesOrDie(buffer.size()));
  EXPECT_DEATH(memory_reader.GetBytesOrDie(1), "Check failed");
}

}  // namespace wifilogd
}  // namespace android