//===-- clang-doc/MergeTest.cpp -------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "ClangDocTest.h"
#include "Representation.h"
#include "gtest/gtest.h"

namespace clang {
namespace doc {

class MergeTest : public ClangDocContextTest {};

TEST_F(MergeTest, mergeNamespaceInfos) {
  NamespaceInfo One;
  One.Name = "Namespace";
  One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Reference RA(NonEmptySID, "ChildNamespace", InfoType::IT_namespace);
  One.Children.Namespaces.push_back(RA);
  One.Children.Records.emplace_back(NonEmptySID, "ChildStruct",
                                    InfoType::IT_record);
  One.Children.Functions.emplace_back();
  One.Children.Functions.back().Name = "OneFunction";
  One.Children.Functions.back().USR = NonEmptySID;
  One.Children.Enums.emplace_back();
  One.Children.Enums.back().Name = "OneEnum";
  One.Children.Enums.back().USR = NonEmptySID;

  NamespaceInfo Two;
  Two.Name = "Namespace";
  Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Reference RB(EmptySID, "OtherChildNamespace", InfoType::IT_namespace);
  Two.Children.Namespaces.push_back(RB);
  Two.Children.Records.emplace_back(EmptySID, "OtherChildStruct",
                                    InfoType::IT_record);
  Two.Children.Functions.emplace_back();
  Two.Children.Functions.back().Name = "TwoFunction";
  Two.Children.Enums.emplace_back();
  Two.Children.Enums.back().Name = "TwoEnum";

  OwningPtrVec<Info> Infos;
  Infos.emplace_back(allocatePtr<NamespaceInfo>(std::move(One)));
  Infos.emplace_back(allocatePtr<NamespaceInfo>(std::move(Two)));

  auto Expected = allocatePtr<NamespaceInfo>();
  Expected->Name = "Namespace";
  Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Reference RC(NonEmptySID, "ChildNamespace", InfoType::IT_namespace);
  Expected->Children.Namespaces.push_back(RC);
  Expected->Children.Records.emplace_back(NonEmptySID, "ChildStruct",
                                          InfoType::IT_record);
  Reference RD(EmptySID, "OtherChildNamespace", InfoType::IT_namespace);
  Expected->Children.Namespaces.push_back(RD);
  Expected->Children.Records.emplace_back(EmptySID, "OtherChildStruct",
                                          InfoType::IT_record);
  Expected->Children.Functions.emplace_back();
  Expected->Children.Functions.back().Name = "OneFunction";
  Expected->Children.Functions.back().USR = NonEmptySID;
  Expected->Children.Functions.emplace_back();
  Expected->Children.Functions.back().Name = "TwoFunction";
  Expected->Children.Enums.emplace_back();
  Expected->Children.Enums.back().Name = "OneEnum";
  Expected->Children.Enums.back().USR = NonEmptySID;
  Expected->Children.Enums.emplace_back();
  Expected->Children.Enums.back().Name = "TwoEnum";

  auto Actual = mergeInfos(Infos);
  assert(Actual);
  CheckNamespaceInfo(InfoAsNamespace(Expected.get()),
                     InfoAsNamespace(Actual.get().get()));
}

TEST_F(MergeTest, mergeRecordInfos) {
  RecordInfo One;
  One.Name = "r";
  One.IsTypeDef = true;
  One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  One.DefLoc = Location(10, 10, "test.cpp");

  One.Members.emplace_back(TypeInfo("int"), "X", AccessSpecifier::AS_private);
  One.TagType = TagTypeKind::Class;
  One.Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
  One.VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);

  One.Bases.emplace_back(EmptySID, "F", "path/to/F", true,
                         AccessSpecifier::AS_protected, true);
  One.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
                                    InfoType::IT_record);
  One.Children.Functions.emplace_back();
  One.Children.Functions.back().Name = "OneFunction";
  One.Children.Functions.back().USR = NonEmptySID;
  One.Children.Enums.emplace_back();
  One.Children.Enums.back().Name = "OneEnum";
  One.Children.Enums.back().USR = NonEmptySID;

  RecordInfo Two;
  Two.Name = "r";
  Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Two.Loc.emplace_back(12, 12, "test.cpp");

  Two.TagType = TagTypeKind::Class;

  Two.Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
                                    InfoType::IT_record, "path");
  Two.Children.Functions.emplace_back();
  Two.Children.Functions.back().Name = "TwoFunction";
  Two.Children.Enums.emplace_back();
  Two.Children.Enums.back().Name = "TwoEnum";

  OwningPtrVec<Info> Infos;
  Infos.emplace_back(allocatePtr<RecordInfo>(std::move(One)));
  Infos.emplace_back(allocatePtr<RecordInfo>(std::move(Two)));

  auto Expected = allocatePtr<RecordInfo>();
  Expected->Name = "r";
  Expected->IsTypeDef = true;
  Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Expected->DefLoc = Location(10, 10, "test.cpp");
  Expected->Loc.emplace_back(12, 12, "test.cpp");

  Expected->Members.emplace_back(TypeInfo("int"), "X",
                                 AccessSpecifier::AS_private);
  Expected->TagType = TagTypeKind::Class;
  Expected->Parents.emplace_back(EmptySID, "F", InfoType::IT_record);
  Expected->VirtualParents.emplace_back(EmptySID, "G", InfoType::IT_record);
  Expected->Bases.emplace_back(EmptySID, "F", "path/to/F", true,
                               AccessSpecifier::AS_protected, true);

  Expected->Children.Records.emplace_back(NonEmptySID, "SharedChildStruct",
                                          InfoType::IT_record, "path");
  Expected->Children.Functions.emplace_back();
  Expected->Children.Functions.back().Name = "OneFunction";
  Expected->Children.Functions.back().USR = NonEmptySID;
  Expected->Children.Functions.emplace_back();
  Expected->Children.Functions.back().Name = "TwoFunction";
  Expected->Children.Enums.emplace_back();
  Expected->Children.Enums.back().Name = "OneEnum";
  Expected->Children.Enums.back().USR = NonEmptySID;
  Expected->Children.Enums.emplace_back();
  Expected->Children.Enums.back().Name = "TwoEnum";

  auto Actual = mergeInfos(Infos);
  assert(Actual);
  CheckRecordInfo(InfoAsRecord(Expected.get()),
                  InfoAsRecord(Actual.get().get()));
}

TEST_F(MergeTest, mergeFunctionInfos) {
  FunctionInfo One;
  One.Name = "f";
  One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  One.DefLoc = Location(10, 10, "test.cpp");
  One.Loc.emplace_back(12, 12, "test.cpp");

  One.IsMethod = true;
  One.Parent = Reference(EmptySID, "Parent", InfoType::IT_namespace);

  CommentInfo OneText[] = {
      CommentInfo(CommentKind::CK_TextComment, {}, "This is a text comment.")};
  CommentInfo OnePara[] = {
      CommentInfo(CommentKind::CK_ParagraphComment, OneText)};
  One.Description.emplace_back(CommentKind::CK_FullComment, OnePara);

  FunctionInfo Two;
  Two.Name = "f";
  Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Two.Loc.emplace_back(12, 12, "test.cpp");

  Two.ReturnType = TypeInfo("void");
  Two.Params.emplace_back(TypeInfo("int"), "P");

  CommentInfo TwoText[] = {
      CommentInfo(CommentKind::CK_TextComment, {}, "This is a text comment.")};
  CommentInfo TwoPara[] = {
      CommentInfo(CommentKind::CK_ParagraphComment, TwoText)};
  Two.Description.emplace_back(CommentKind::CK_FullComment, TwoPara);

  OwningPtrVec<Info> Infos;
  Infos.emplace_back(allocatePtr<FunctionInfo>(std::move(One)));
  Infos.emplace_back(allocatePtr<FunctionInfo>(std::move(Two)));

  auto Expected = allocatePtr<FunctionInfo>();
  Expected->Name = "f";
  Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Expected->DefLoc = Location(10, 10, "test.cpp");
  Expected->Loc.emplace_back(12, 12, "test.cpp");

  Expected->ReturnType = TypeInfo("void");
  Expected->Params.emplace_back(TypeInfo("int"), "P");
  Expected->IsMethod = true;
  Expected->Parent = Reference(EmptySID, "Parent", InfoType::IT_namespace);

  CommentInfo ExpectedText[] = {
      CommentInfo(CommentKind::CK_TextComment, {}, "This is a text comment.")};
  CommentInfo ExpectedPara[] = {
      CommentInfo(CommentKind::CK_ParagraphComment, ExpectedText)};
  Expected->Description.emplace_back(CommentKind::CK_FullComment, ExpectedPara);

  auto Actual = mergeInfos(Infos);
  assert(Actual);
  CheckFunctionInfo(InfoAsFunction(Expected.get()),
                    InfoAsFunction(Actual.get().get()));
}

TEST_F(MergeTest, mergeEnumInfos) {
  EnumInfo One;
  One.Name = "e";
  One.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  One.DefLoc = Location(10, 10, "test.cpp");
  One.Loc.emplace_back(12, 12, "test.cpp");

  One.Scoped = true;

  EnumInfo Two;
  Two.Name = "e";
  Two.Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Two.Loc.emplace_back(20, 20, "test.cpp");

  Two.Members.emplace_back("X");
  Two.Members.emplace_back("Y");

  OwningPtrVec<Info> Infos;
  Infos.emplace_back(allocatePtr<EnumInfo>(std::move(One)));
  Infos.emplace_back(allocatePtr<EnumInfo>(std::move(Two)));

  auto Expected = allocatePtr<EnumInfo>();
  Expected->Name = "e";
  Expected->Namespace.emplace_back(EmptySID, "A", InfoType::IT_namespace);

  Expected->DefLoc = Location(10, 10, "test.cpp");
  Expected->Loc.emplace_back(12, 12, "test.cpp");
  Expected->Loc.emplace_back(20, 20, "test.cpp");

  Expected->Members.emplace_back("X");
  Expected->Members.emplace_back("Y");
  Expected->Scoped = true;

  auto Actual = mergeInfos(Infos);
  assert(Actual);
  CheckEnumInfo(InfoAsEnum(Expected.get()), InfoAsEnum(Actual.get().get()));
}

} // namespace doc
} // namespace clang
