// Copyright (c) 2013 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/quic/core/quic_error_codes.h"

#include <cstdint>

#include "openssl/ssl.h"
#include "quiche/quic/platform/api/quic_test.h"

namespace quic {
namespace test {
namespace {

using QuicErrorCodesTest = QuicTest;

TEST_F(QuicErrorCodesTest, QuicErrorCodeToString) {
  EXPECT_STREQ("QUIC_NO_ERROR", QuicErrorCodeToString(QUIC_NO_ERROR));
}

TEST_F(QuicErrorCodesTest, QuicIetfTransportErrorCodeString) {
  EXPECT_EQ("CRYPTO_ERROR(missing extension)",
            QuicIetfTransportErrorCodeString(
                static_cast<quic::QuicIetfTransportErrorCodes>(
                    CRYPTO_ERROR_FIRST + SSL_AD_MISSING_EXTENSION)));

  EXPECT_EQ("NO_IETF_QUIC_ERROR",
            QuicIetfTransportErrorCodeString(NO_IETF_QUIC_ERROR));
  EXPECT_EQ("INTERNAL_ERROR", QuicIetfTransportErrorCodeString(INTERNAL_ERROR));
  EXPECT_EQ("SERVER_BUSY_ERROR",
            QuicIetfTransportErrorCodeString(SERVER_BUSY_ERROR));
  EXPECT_EQ("FLOW_CONTROL_ERROR",
            QuicIetfTransportErrorCodeString(FLOW_CONTROL_ERROR));
  EXPECT_EQ("STREAM_LIMIT_ERROR",
            QuicIetfTransportErrorCodeString(STREAM_LIMIT_ERROR));
  EXPECT_EQ("STREAM_STATE_ERROR",
            QuicIetfTransportErrorCodeString(STREAM_STATE_ERROR));
  EXPECT_EQ("FINAL_SIZE_ERROR",
            QuicIetfTransportErrorCodeString(FINAL_SIZE_ERROR));
  EXPECT_EQ("FRAME_ENCODING_ERROR",
            QuicIetfTransportErrorCodeString(FRAME_ENCODING_ERROR));
  EXPECT_EQ("TRANSPORT_PARAMETER_ERROR",
            QuicIetfTransportErrorCodeString(TRANSPORT_PARAMETER_ERROR));
  EXPECT_EQ("CONNECTION_ID_LIMIT_ERROR",
            QuicIetfTransportErrorCodeString(CONNECTION_ID_LIMIT_ERROR));
  EXPECT_EQ("PROTOCOL_VIOLATION",
            QuicIetfTransportErrorCodeString(PROTOCOL_VIOLATION));
  EXPECT_EQ("INVALID_TOKEN", QuicIetfTransportErrorCodeString(INVALID_TOKEN));
  EXPECT_EQ("CRYPTO_BUFFER_EXCEEDED",
            QuicIetfTransportErrorCodeString(CRYPTO_BUFFER_EXCEEDED));
  EXPECT_EQ("KEY_UPDATE_ERROR",
            QuicIetfTransportErrorCodeString(KEY_UPDATE_ERROR));
  EXPECT_EQ("AEAD_LIMIT_REACHED",
            QuicIetfTransportErrorCodeString(AEAD_LIMIT_REACHED));

  EXPECT_EQ("Unknown(1024)",
            QuicIetfTransportErrorCodeString(
                static_cast<quic::QuicIetfTransportErrorCodes>(0x400)));
}

TEST_F(QuicErrorCodesTest, QuicErrorCodeToTransportErrorCode) {
  for (uint32_t internal_error_code = 0; internal_error_code < QUIC_LAST_ERROR;
       ++internal_error_code) {
    std::string internal_error_code_string =
        QuicErrorCodeToString(static_cast<QuicErrorCode>(internal_error_code));
    if (internal_error_code_string == "INVALID_ERROR_CODE") {
      // Not a valid QuicErrorCode.
      continue;
    }
    QuicErrorCodeToIetfMapping ietf_error_code =
        QuicErrorCodeToTransportErrorCode(
            static_cast<QuicErrorCode>(internal_error_code));
    if (ietf_error_code.is_transport_close) {
      QuicIetfTransportErrorCodes transport_error_code =
          static_cast<QuicIetfTransportErrorCodes>(ietf_error_code.error_code);
      bool is_transport_crypto_error_code =
          transport_error_code >= 0x100 && transport_error_code <= 0x1ff;
      if (is_transport_crypto_error_code) {
        // Ensure that every QuicErrorCode that maps to a CRYPTO_ERROR code has
        // a corresponding reverse mapping in TlsAlertToQuicErrorCode:
        EXPECT_EQ(
            internal_error_code,
            TlsAlertToQuicErrorCode(transport_error_code - CRYPTO_ERROR_FIRST));
      }
      bool is_valid_transport_error_code =
          transport_error_code <= 0x0f || is_transport_crypto_error_code;
      EXPECT_TRUE(is_valid_transport_error_code) << internal_error_code_string;
    } else {
      // Non-transport errors are application errors, either HTTP/3 or QPACK.
      uint64_t application_error_code = ietf_error_code.error_code;
      bool is_valid_http3_error_code =
          application_error_code >= 0x100 && application_error_code <= 0x110;
      bool is_valid_qpack_error_code =
          application_error_code >= 0x200 && application_error_code <= 0x202;
      EXPECT_TRUE(is_valid_http3_error_code || is_valid_qpack_error_code)
          << internal_error_code_string;
    }
  }
}

using QuicRstErrorCodesTest = QuicTest;

TEST_F(QuicRstErrorCodesTest, QuicRstStreamErrorCodeToString) {
  EXPECT_STREQ("QUIC_BAD_APPLICATION_PAYLOAD",
               QuicRstStreamErrorCodeToString(QUIC_BAD_APPLICATION_PAYLOAD));
}

// When an IETF application protocol error code (sent on the wire in
// RESET_STREAM and STOP_SENDING frames) is translated into a
// QuicRstStreamErrorCode and back, it must yield the original value.
TEST_F(QuicRstErrorCodesTest,
       IetfResetStreamErrorCodeToRstStreamErrorCodeAndBack) {
  for (uint64_t wire_code :
       {static_cast<uint64_t>(QuicHttp3ErrorCode::HTTP3_NO_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::GENERAL_PROTOCOL_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::INTERNAL_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::STREAM_CREATION_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::CLOSED_CRITICAL_STREAM),
        static_cast<uint64_t>(QuicHttp3ErrorCode::FRAME_UNEXPECTED),
        static_cast<uint64_t>(QuicHttp3ErrorCode::FRAME_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::EXCESSIVE_LOAD),
        static_cast<uint64_t>(QuicHttp3ErrorCode::ID_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::SETTINGS_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::MISSING_SETTINGS),
        static_cast<uint64_t>(QuicHttp3ErrorCode::REQUEST_REJECTED),
        static_cast<uint64_t>(QuicHttp3ErrorCode::REQUEST_CANCELLED),
        static_cast<uint64_t>(QuicHttp3ErrorCode::REQUEST_INCOMPLETE),
        static_cast<uint64_t>(QuicHttp3ErrorCode::CONNECT_ERROR),
        static_cast<uint64_t>(QuicHttp3ErrorCode::VERSION_FALLBACK),
        static_cast<uint64_t>(QuicHttpQpackErrorCode::DECOMPRESSION_FAILED),
        static_cast<uint64_t>(QuicHttpQpackErrorCode::ENCODER_STREAM_ERROR),
        static_cast<uint64_t>(QuicHttpQpackErrorCode::DECODER_STREAM_ERROR)}) {
    QuicRstStreamErrorCode rst_stream_error_code =
        IetfResetStreamErrorCodeToRstStreamErrorCode(wire_code);
    EXPECT_EQ(wire_code, RstStreamErrorCodeToIetfResetStreamErrorCode(
                             rst_stream_error_code));
  }
}

}  // namespace
}  // namespace test
}  // namespace quic
