//===-- 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);

  One.Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace",
                                       InfoType::IT_namespace);
  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);

  Two.Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace",
                                       InfoType::IT_namespace);
  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";

  std::vector<std::unique_ptr<Info>> Infos;
  Infos.emplace_back(std::make_unique<NamespaceInfo>(std::move(One)));
  Infos.emplace_back(std::make_unique<NamespaceInfo>(std::move(Two)));

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

  Expected->Children.Namespaces.emplace_back(NonEmptySID, "ChildNamespace",
                                             InfoType::IT_namespace);
  Expected->Children.Records.emplace_back(NonEmptySID, "ChildStruct",
                                          InfoType::IT_record);
  Expected->Children.Namespaces.emplace_back(EmptySID, "OtherChildNamespace",
                                             InfoType::IT_namespace);
  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";

  std::vector<std::unique_ptr<Info>> Infos;
  Infos.emplace_back(std::make_unique<RecordInfo>(std::move(One)));
  Infos.emplace_back(std::make_unique<RecordInfo>(std::move(Two)));

  auto Expected = std::make_unique<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);

  One.Description.emplace_back();
  auto *OneFullComment = &One.Description.back();
  OneFullComment->Kind = CommentKind::CK_FullComment;
  auto OneParagraphComment = std::make_unique<CommentInfo>();
  OneParagraphComment->Kind = CommentKind::CK_ParagraphComment;
  auto OneTextComment = std::make_unique<CommentInfo>();
  OneTextComment->Kind = CommentKind::CK_TextComment;
  OneTextComment->Text = "This is a text comment.";
  OneParagraphComment->Children.push_back(std::move(OneTextComment));
  OneFullComment->Children.push_back(std::move(OneParagraphComment));

  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");

  Two.Description.emplace_back();
  auto *TwoFullComment = &Two.Description.back();
  TwoFullComment->Kind = CommentKind::CK_FullComment;
  auto TwoParagraphComment = std::make_unique<CommentInfo>();
  TwoParagraphComment->Kind = CommentKind::CK_ParagraphComment;
  auto TwoTextComment = std::make_unique<CommentInfo>();
  TwoTextComment->Kind = CommentKind::CK_TextComment;
  TwoTextComment->Text = "This is a text comment.";
  TwoParagraphComment->Children.push_back(std::move(TwoTextComment));
  TwoFullComment->Children.push_back(std::move(TwoParagraphComment));

  std::vector<std::unique_ptr<Info>> Infos;
  Infos.emplace_back(std::make_unique<FunctionInfo>(std::move(One)));
  Infos.emplace_back(std::make_unique<FunctionInfo>(std::move(Two)));

  auto Expected = std::make_unique<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);

  Expected->Description.emplace_back();
  auto *ExpectedFullComment = &Expected->Description.back();
  ExpectedFullComment->Kind = CommentKind::CK_FullComment;
  auto ExpectedParagraphComment = std::make_unique<CommentInfo>();
  ExpectedParagraphComment->Kind = CommentKind::CK_ParagraphComment;
  auto ExpectedTextComment = std::make_unique<CommentInfo>();
  ExpectedTextComment->Kind = CommentKind::CK_TextComment;
  ExpectedTextComment->Text = "This is a text comment.";
  ExpectedParagraphComment->Children.push_back(std::move(ExpectedTextComment));
  ExpectedFullComment->Children.push_back(std::move(ExpectedParagraphComment));

  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");

  std::vector<std::unique_ptr<Info>> Infos;
  Infos.emplace_back(std::make_unique<EnumInfo>(std::move(One)));
  Infos.emplace_back(std::make_unique<EnumInfo>(std::move(Two)));

  auto Expected = std::make_unique<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
