// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC.  All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd

#include "upb/message/internal/compare_unknown.h"

#include <stdint.h>

#include <cstddef>
#include <initializer_list>
#include <string>

#include <gtest/gtest.h>
#include "absl/cleanup/cleanup.h"
#include "google/protobuf/test_messages_proto2.upb.h"
#include "upb/base/string_view.h"
#include "upb/base/upcast.h"
#include "upb/mem/alloc.h"
#include "upb/mem/arena.h"
#include "upb/mem/arena.hpp"
#include "upb/message/compare.h"
#include "upb/message/internal/accessors.h"
#include "upb/message/internal/message.h"
#include "upb/message/message.h"
#include "upb/message/test.upb.h"
#include "upb/message/test.upb_minitable.h"
#include "upb/message/unknown_fields.h"
#include "upb/test/test.upb.h"
#include "upb/test/test.upb_minitable.h"
#include "upb/wire/encode.h"
#include "upb/wire/test_util/wire_message.h"

// Must be last.
#include "upb/port/def.inc"

namespace upb {
namespace test {

namespace {

using ::upb::test::wire_types::Delimited;
using ::upb::test::wire_types::Fixed32;
using ::upb::test::wire_types::Fixed64;
using ::upb::test::wire_types::Group;
using ::upb::test::wire_types::Varint;
using ::upb::test::wire_types::WireMessage;

upb_UnknownCompareResult CompareUnknownWithMaxDepth(
    WireMessage uf1, WireMessage uf2, int max_depth, int min_tag_length = 1,
    int min_val_varint_length = 1) {
  upb::Arena arena1;
  upb::Arena arena2;
  protobuf_test_messages_proto2_TestAllTypesProto2* msg1 =
      protobuf_test_messages_proto2_TestAllTypesProto2_new(arena1.ptr());
  protobuf_test_messages_proto2_TestAllTypesProto2* msg2 =
      protobuf_test_messages_proto2_TestAllTypesProto2_new(arena2.ptr());
  // Add the unknown fields to the messages.
  std::string buf1 = ToBinaryPayloadWithLongVarints(uf1, min_tag_length,
                                                    min_val_varint_length);
  std::string buf2 = ToBinaryPayloadWithLongVarints(uf2, min_tag_length,
                                                    min_val_varint_length);
  EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
      UPB_UPCAST(msg1), buf1.data(), buf1.size(), arena1.ptr(),
      kUpb_AddUnknown_Copy));
  EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
      UPB_UPCAST(msg2), buf2.data(), buf2.size(), arena2.ptr(),
      kUpb_AddUnknown_Copy));
  return _upb_Message_UnknownFieldsAreEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                                            max_depth);
}

upb_UnknownCompareResult CompareUnknown(WireMessage uf1, WireMessage uf2) {
  return CompareUnknownWithMaxDepth(uf1, uf2, 64);
}

TEST(CompareTest, UnknownFieldsReflexive) {
  EXPECT_EQ(kUpb_UnknownCompareResult_Equal, CompareUnknown({}, {}));
  EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
            CompareUnknown({{1, Varint(123)}, {2, Fixed32(456)}},
                           {{1, Varint(123)}, {2, Fixed32(456)}}));
  EXPECT_EQ(
      kUpb_UnknownCompareResult_Equal,
      CompareUnknown(
          {{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}},
          {{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}}));
}

TEST(CompareTest, UnknownFieldsOrdering) {
  EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
            CompareUnknown({{1, Varint(111)},
                            {2, Delimited("ABC")},
                            {3, Fixed32(456)},
                            {4, Fixed64(123)},
                            {5, Group({})}},
                           {{5, Group({})},
                            {4, Fixed64(123)},
                            {3, Fixed32(456)},
                            {2, Delimited("ABC")},
                            {1, Varint(111)}}));
  EXPECT_EQ(kUpb_UnknownCompareResult_NotEqual,
            CompareUnknown({{1, Varint(111)},
                            {2, Delimited("ABC")},
                            {3, Fixed32(456)},
                            {4, Fixed64(123)},
                            {5, Group({})}},
                           {{5, Group({})},
                            {4, Fixed64(123)},
                            {3, Fixed32(455)},  // Small difference.
                            {2, Delimited("ABC")},
                            {1, Varint(111)}}));
  EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
            CompareUnknown({{3, Fixed32(456)}, {4, Fixed64(123)}},
                           {{4, Fixed64(123)}, {3, Fixed32(456)}}));
  EXPECT_EQ(
      kUpb_UnknownCompareResult_Equal,
      CompareUnknown(
          {{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}},
          {{1, Group({{2, Group({{4, Fixed64(123)}, {3, Fixed32(456)}})}})}}));
}

TEST(CompareTest, LongVarint) {
  EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
            CompareUnknownWithMaxDepth({{1, Varint(123)}, {2, Varint(456)}},
                                       {{1, Varint(123)}, {2, Varint(456)}}, 64,
                                       5, 10));
  EXPECT_EQ(kUpb_UnknownCompareResult_Equal,
            CompareUnknownWithMaxDepth({{2, Varint(456)}, {1, Varint(123)}},
                                       {{1, Varint(123)}, {2, Varint(456)}}, 64,
                                       5, 10));
}

TEST(CompareTest, MaxDepth) {
  EXPECT_EQ(
      kUpb_UnknownCompareResult_MaxDepthExceeded,
      CompareUnknownWithMaxDepth(
          {{1, Group({{2, Group({{3, Fixed32(456)}, {4, Fixed64(123)}})}})}},
          {{1, Group({{2, Group({{4, Fixed64(123)}, {3, Fixed32(456)}})}})}},
          1));
}

TEST(CompareTest, MessageIsEqualWithIdenticalNonCanonicalExtensions) {
  upb::Arena arena;

  // 1. Create base msg1 and msg2 which start empty
  upb_test_ModelWithExtensions* msg1 =
      upb_test_ModelWithExtensions_new(arena.ptr());
  upb_test_ModelWithExtensions* msg2 =
      upb_test_ModelWithExtensions_new(arena.ptr());

  // 2. Create sub-message value ("World")
  upb_test_ModelExtension1* extension1 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(extension1,
                                   upb_StringView_FromString("World"));

  upb_test_ModelExtension1* extension2 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(extension2,
                                   upb_StringView_FromString("World"));

  // 3. Attach identical non-canonical extensions to both messages
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
      arena.ptr());
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg2), upb_test_ModelExtension1_model_ext_ext, &extension2,
      arena.ptr());

  // 4. Verify upb_Message_IsEqual yields true
  bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                                      &upb_0test__ModelWithExtensions_msg_init,
                                      kUpb_CompareOption_IncludeUnknownFields);
  EXPECT_TRUE(is_equal);
}

TEST(CompareTest, MessageIsEqualWithDifferentNonCanonicalExtensions) {
  upb::Arena arena;

  // 1. Create base msg1 and msg2 which start empty
  upb_test_ModelWithExtensions* msg1 =
      upb_test_ModelWithExtensions_new(arena.ptr());
  upb_test_ModelWithExtensions* msg2 =
      upb_test_ModelWithExtensions_new(arena.ptr());

  // 2. Create custom sub-message 1 ("World")
  upb_test_ModelExtension1* extension1 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(extension1,
                                   upb_StringView_FromString("World"));

  // 3. Create custom sub-message 2 ("Hello")
  upb_test_ModelExtension1* extension2 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(extension2,
                                   upb_StringView_FromString("Hello"));

  // 4. Attach different non-canonical extensions to the two messages
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
      arena.ptr());
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg2), upb_test_ModelExtension1_model_ext_ext, &extension2,
      arena.ptr());

  // Verify that NextUnknown2 successfully yields the non-canonical extension
  upb_MessageUnknown udata;
  uintptr_t uiter = kUpb_Message_UnknownBegin;
  ASSERT_TRUE(upb_Message_NextUnknown2(UPB_UPCAST(msg1), &udata, &uiter));
  EXPECT_EQ(udata.type, kUpb_MessageUnknownType_NonCanonicalExtension);

  // 5. Verify upb_Message_IsEqual yields false because they are different!
  bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                                      &upb_0test__ModelWithExtensions_msg_init,
                                      kUpb_CompareOption_IncludeUnknownFields);
  EXPECT_FALSE(is_equal);
}

TEST(CompareTest, MessageIsEqualWithOnlyOneHavingNonCanonicalExtension) {
  upb::Arena arena;

  // 1. Create base msg1 and msg2
  upb_test_ModelWithExtensions* msg1 =
      upb_test_ModelWithExtensions_new(arena.ptr());
  upb_test_ModelWithExtensions* msg2 =
      upb_test_ModelWithExtensions_new(arena.ptr());

  // 2. Create parsed submessage ("World")
  upb_test_ModelExtension1* extension1 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(extension1,
                                   upb_StringView_FromString("World"));

  // 3. Attach only to msg1
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
      arena.ptr());

  // 4. Verify upb_Message_IsEqual yields false with IncludeUnknownFields
  bool is_equal_with_unknowns =
      upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                          &upb_0test__ModelWithExtensions_msg_init,
                          kUpb_CompareOption_IncludeUnknownFields);
  EXPECT_FALSE(is_equal_with_unknowns);

  // 5. Without IncludeUnknownFields, they compare equal
  bool is_equal_without_unknowns =
      upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                          &upb_0test__ModelWithExtensions_msg_init, 0);
  EXPECT_TRUE(is_equal_without_unknowns);
}

TEST(CompareTest, MessageIsEqualWithNonCanonicalExtensionMatchingRawUnknown) {
  upb::Arena arena;

  // 1. Create custom sub-message ("World")
  upb_test_ModelExtension1* extension1 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(extension1,
                                   upb_StringView_FromString("World"));

  // 2. Obtain encoded raw wire bytes of Extension A by serializing tmp_msg
  upb_test_ModelWithExtensions* tmp_msg =
      upb_test_ModelWithExtensions_new(arena.ptr());
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(tmp_msg), upb_test_ModelExtension1_model_ext_ext, &extension1,
      arena.ptr());

  char* buf;
  size_t size;
  upb_EncodeStatus enc_status =
      upb_Encode(UPB_UPCAST(tmp_msg), &upb_0test__ModelWithExtensions_msg_init,
                 0, arena.ptr(), &buf, &size);
  ASSERT_EQ(enc_status, kUpb_EncodeStatus_Ok);
  ASSERT_GT(size, 0u);

  // 3. Create msg1 with non-canonical extension A
  upb_test_ModelWithExtensions* msg1 =
      upb_test_ModelWithExtensions_new(arena.ptr());
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg1), upb_test_ModelExtension1_model_ext_ext, &extension1,
      arena.ptr());

  // 4. Create msg2 with raw unknown bytes representing A
  upb_test_ModelWithExtensions* msg2 =
      upb_test_ModelWithExtensions_new(arena.ptr());
  EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
      UPB_UPCAST(msg2), buf, size, arena.ptr(), kUpb_AddUnknown_Copy));

  // 5. Verify they compare equal under IncludeUnknownFields
  bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                                      &upb_0test__ModelWithExtensions_msg_init,
                                      kUpb_CompareOption_IncludeUnknownFields);
  EXPECT_TRUE(is_equal);
}

TEST(CompareTest, MessageSetNonCanonicalExtMatchesUnknown) {
  upb::Arena arena;

  // 1. Create custom sub-message (MessageSetMember)
  upb_test_MessageSetMember* extension1 =
      upb_test_MessageSetMember_new(arena.ptr());
  upb_test_MessageSetMember_set_optional_int32(extension1, 123);

  // 2. Obtain encoded raw wire bytes of Extension A by serializing tmp_msg
  upb_test_TestMessageSet* tmp_msg = upb_test_TestMessageSet_new(arena.ptr());
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(tmp_msg), upb_test_MessageSetMember_message_set_extension_ext,
      &extension1, arena.ptr());

  char* buf;
  size_t size;
  upb_EncodeStatus enc_status =
      upb_Encode(UPB_UPCAST(tmp_msg), &upb_0test__TestMessageSet_msg_init, 0,
                 arena.ptr(), &buf, &size);
  ASSERT_EQ(enc_status, kUpb_EncodeStatus_Ok);
  ASSERT_GT(size, 0u);

  // 3. Create msg1 with non-canonical extension A
  upb_test_TestMessageSet* msg1 = upb_test_TestMessageSet_new(arena.ptr());
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg1), upb_test_MessageSetMember_message_set_extension_ext,
      &extension1, arena.ptr());

  // 4. Create msg2 with raw unknown bytes representing A
  upb_test_TestMessageSet* msg2 = upb_test_TestMessageSet_new(arena.ptr());
  EXPECT_TRUE(UPB_PRIVATE(_upb_Message_AddUnknown)(
      UPB_UPCAST(msg2), buf, size, arena.ptr(), kUpb_AddUnknown_Copy));

  // 5. Verify they compare equal under IncludeUnknownFields
  bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                                      &upb_0test__TestMessageSet_msg_init,
                                      kUpb_CompareOption_IncludeUnknownFields);
  EXPECT_TRUE(is_equal);
}

TEST(CompareTest, MessageIsEqualWithCanonicalAndNonCanonicalExtensions) {
  upb::Arena arena;

  // 1. Create msg1 and set canonical extension
  upb_test_ModelWithExtensions* msg1 =
      upb_test_ModelWithExtensions_new(arena.ptr());
  upb_test_ModelExtension1* ext_msg1 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(ext_msg1,
                                   upb_StringView_FromString("World"));
  upb_test_ModelExtension1_set_model_ext(msg1, ext_msg1, arena.ptr());

  // 2. Create msg2 and set identical non-canonical extension
  upb_test_ModelWithExtensions* msg2 =
      upb_test_ModelWithExtensions_new(arena.ptr());
  upb_test_ModelExtension1* ext_msg2 =
      upb_test_ModelExtension1_new(arena.ptr());
  upb_test_ModelExtension1_set_str(ext_msg2,
                                   upb_StringView_FromString("World"));
  UPB_PRIVATE(_upb_Message_SetNonCanonicalExtension)(
      UPB_UPCAST(msg2), upb_test_ModelExtension1_model_ext_ext, &ext_msg2,
      arena.ptr());

  // 3. Verify upb_Message_IsEqual registers them as NOT equal
  bool is_equal = upb_Message_IsEqual(UPB_UPCAST(msg1), UPB_UPCAST(msg2),
                                      &upb_0test__ModelWithExtensions_msg_init,
                                      kUpb_CompareOption_IncludeUnknownFields);
  EXPECT_FALSE(is_equal);
}

}  // namespace

TEST(CompareTest, AllocationFailure) {
  if (!upb_AllocationCount_IsAvailable()) return;

  auto RunScenario = [&]() -> bool {
    upb_Arena* arena1 = upb_Arena_New();
    if (!arena1) return false;
    auto cleanup1 = absl::MakeCleanup([arena1] { upb_Arena_Free(arena1); });

    upb_Arena* arena2 = upb_Arena_New();
    if (!arena2) return false;
    auto cleanup2 = absl::MakeCleanup([arena2] { upb_Arena_Free(arena2); });

    protobuf_test_messages_proto2_TestAllTypesProto2* msg1 =
        protobuf_test_messages_proto2_TestAllTypesProto2_new(arena1);
    if (!msg1) return false;
    protobuf_test_messages_proto2_TestAllTypesProto2* msg2 =
        protobuf_test_messages_proto2_TestAllTypesProto2_new(arena2);
    if (!msg2) return false;

    WireMessage uf1 = {{2, Fixed32(456)}, {1, Fixed64(123)}};
    WireMessage uf2 = {{2, Fixed32(456)}, {1, Fixed64(123)}};
    std::string buf1 = ToBinaryPayloadWithLongVarints(uf1, 1, 1);
    std::string buf2 = ToBinaryPayloadWithLongVarints(uf2, 1, 1);

    if (!UPB_PRIVATE(_upb_Message_AddUnknown)(UPB_UPCAST(msg1), buf1.data(),
                                              buf1.size(), arena1,
                                              kUpb_AddUnknown_Copy)) {
      return false;
    }
    if (!UPB_PRIVATE(_upb_Message_AddUnknown)(UPB_UPCAST(msg2), buf2.data(),
                                              buf2.size(), arena2,
                                              kUpb_AddUnknown_Copy)) {
      return false;
    }
    upb_UnknownCompareResult res = _upb_Message_UnknownFieldsAreEqual(
        UPB_UPCAST(msg1), UPB_UPCAST(msg2), 64);
    return res == kUpb_UnknownCompareResult_Equal;
  };

  upb_AllocationCount_Reset();
  if (RunScenario()) {
    size_t total_allocations = upb_AllocationCount_Get();
    for (size_t i = 0; i < total_allocations; ++i) {
      upb_AllocationCount_Reset();
      upb_AllocationCount_FailOn(i);
      bool success_with_fail = RunScenario();
      EXPECT_FALSE(success_with_fail)
          << "Unknown comparison unexpectedly succeeded when allocation "
          << "number " << i << " was failed.";
    }
  }
  upb_AllocationCount_Reset();
}

}  // namespace test
}  // namespace upb
