// Copyright (c) 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "quiche/common/test_tools/quiche_test_utils.h"

#include <algorithm>
#include <memory>
#include <string>

#include "quiche/common/platform/api/quiche_googleurl.h"
#include "quiche/common/platform/api/quiche_logging.h"
#include "quiche/common/platform/api/quiche_test.h"

namespace {

std::string HexDumpWithMarks(const char* data, int length, const bool* marks,
                             int mark_length) {
  static const char kHexChars[] = "0123456789abcdef";
  static const int kColumns = 4;

  const int kSizeLimit = 1024;
  if (length > kSizeLimit || mark_length > kSizeLimit) {
    QUICHE_LOG(ERROR) << "Only dumping first " << kSizeLimit << " bytes.";
    length = std::min(length, kSizeLimit);
    mark_length = std::min(mark_length, kSizeLimit);
  }

  std::string hex;
  for (const char* row = data; length > 0;
       row += kColumns, length -= kColumns) {
    for (const char* p = row; p < row + 4; ++p) {
      if (p < row + length) {
        const bool mark =
            (marks && (p - data) < mark_length && marks[p - data]);
        hex += mark ? '*' : ' ';
        hex += kHexChars[(*p & 0xf0) >> 4];
        hex += kHexChars[*p & 0x0f];
        hex += mark ? '*' : ' ';
      } else {
        hex += "    ";
      }
    }
    hex = hex + "  ";

    for (const char* p = row; p < row + 4 && p < row + length; ++p) {
      hex += (*p >= 0x20 && *p < 0x7f) ? (*p) : '.';
    }

    hex = hex + '\n';
  }
  return hex;
}

}  // namespace

namespace quiche {
namespace test {

void CompareCharArraysWithHexError(const std::string& description,
                                   const char* actual, const int actual_len,
                                   const char* expected,
                                   const int expected_len) {
  EXPECT_EQ(actual_len, expected_len);
  const int min_len = std::min(actual_len, expected_len);
  const int max_len = std::max(actual_len, expected_len);
  std::unique_ptr<bool[]> marks(new bool[max_len]);
  bool identical = (actual_len == expected_len);
  for (int i = 0; i < min_len; ++i) {
    if (actual[i] != expected[i]) {
      marks[i] = true;
      identical = false;
    } else {
      marks[i] = false;
    }
  }
  for (int i = min_len; i < max_len; ++i) {
    marks[i] = true;
  }
  if (identical) return;
  ADD_FAILURE() << "Description:\n"
                << description << "\n\nExpected:\n"
                << HexDumpWithMarks(expected, expected_len, marks.get(),
                                    max_len)
                << "\nActual:\n"
                << HexDumpWithMarks(actual, actual_len, marks.get(), max_len);
}

iovec MakeIOVector(absl::string_view str) {
  return iovec{const_cast<char*>(str.data()), static_cast<size_t>(str.size())};
}

bool GoogleUrlSupportsIdnaForTest() {
  const std::string kTestInput = "https://\xe5\x85\x89.example.org/";
  const std::string kExpectedOutput = "https://xn--54q.example.org/";

  GURL url(kTestInput);
  bool valid = url.is_valid() && url.spec() == kExpectedOutput;
  QUICHE_CHECK(valid || !url.is_valid()) << url.spec();
  return valid;
}

}  // namespace test
}  // namespace quiche
