//===-- ObjCLanguageTest.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 "Plugins/Language/ObjC/ObjCLanguage.h"
#include "lldb/lldb-enumerations.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include <optional>

#include "llvm/ADT/StringRef.h"

using namespace lldb_private;

TEST(ObjCLanguage, MethodNameParsing) {
  struct TestCase {
    llvm::StringRef input;
    llvm::StringRef full_name_sans_category;
    llvm::StringRef class_name;
    llvm::StringRef class_name_with_category;
    llvm::StringRef category;
    llvm::StringRef selector;
  };

  TestCase strict_cases[] = {
      {"-[MyClass mySelector:]", "", "MyClass", "MyClass", "", "mySelector:"},
      {"+[MyClass mySelector:]", "", "MyClass", "MyClass", "", "mySelector:"},
      {"-[MyClass(my_category) mySelector:]", "-[MyClass mySelector:]",
       "MyClass", "MyClass(my_category)", "my_category", "mySelector:"},
      {"+[MyClass(my_category) mySelector:]", "+[MyClass mySelector:]",
       "MyClass", "MyClass(my_category)", "my_category", "mySelector:"},
  };

  TestCase lax_cases[] = {
      {"[MyClass mySelector:]", "", "MyClass", "MyClass", "", "mySelector:"},
      {"[MyClass(my_category) mySelector:]", "[MyClass mySelector:]", "MyClass",
       "MyClass(my_category)", "my_category", "mySelector:"},
  };

  // First, be strict
  for (const auto &test : strict_cases) {
    std::optional<const ObjCLanguage::ObjCMethodName> method =
        ObjCLanguage::ObjCMethodName::Create(test.input, /*strict = */ true);
    EXPECT_TRUE(method.has_value());
    EXPECT_EQ(test.full_name_sans_category,
              method->GetFullNameWithoutCategory());
    EXPECT_EQ(test.class_name, method->GetClassName());
    EXPECT_EQ(test.class_name_with_category,
              method->GetClassNameWithCategory());
    EXPECT_EQ(test.category, method->GetCategory());
    EXPECT_EQ(test.selector, method->GetSelector());
  }

  // We should make sure strict parsing does not accept lax cases
  for (const auto &test : lax_cases) {
    std::optional<const ObjCLanguage::ObjCMethodName> method =
        ObjCLanguage::ObjCMethodName::Create(test.input, /*strict = */ true);
    EXPECT_FALSE(method.has_value());
  }

  // All strict cases should work when not lax
  for (const auto &test : strict_cases) {
    std::optional<const ObjCLanguage::ObjCMethodName> method =
        ObjCLanguage::ObjCMethodName::Create(test.input, /*strict = */ false);
    EXPECT_TRUE(method.has_value());
    EXPECT_EQ(test.full_name_sans_category,
              method->GetFullNameWithoutCategory());
    EXPECT_EQ(test.class_name, method->GetClassName());
    EXPECT_EQ(test.class_name_with_category,
              method->GetClassNameWithCategory());
    EXPECT_EQ(test.category, method->GetCategory());
    EXPECT_EQ(test.selector, method->GetSelector());
  }

  // Make sure non-strict parsing works
  for (const auto &test : lax_cases) {
    std::optional<const ObjCLanguage::ObjCMethodName> method =
        ObjCLanguage::ObjCMethodName::Create(test.input, /*strict = */ false);
    EXPECT_TRUE(method.has_value());
    EXPECT_EQ(test.full_name_sans_category,
              method->GetFullNameWithoutCategory());
    EXPECT_EQ(test.class_name, method->GetClassName());
    EXPECT_EQ(test.class_name_with_category,
              method->GetClassNameWithCategory());
    EXPECT_EQ(test.category, method->GetCategory());
    EXPECT_EQ(test.selector, method->GetSelector());
  }
}

TEST(ObjCLanguage, InvalidMethodNameParsing) {
  // Tests that we correctly reject malformed function names

  llvm::StringRef test_cases[] = {"+[Uh oh!",
                                  "-[Definitely not...",
                                  "[Nice try ] :)",
                                  "+MaybeIfYouSquintYourEyes]",
                                  "?[Tricky]",
                                  "+[]",
                                  "-[]",
                                  "[]"};

  for (const auto &name : test_cases) {
    std::optional<const ObjCLanguage::ObjCMethodName> strict_method =
        ObjCLanguage::ObjCMethodName::Create(name, /*strict = */ false);
    EXPECT_FALSE(strict_method.has_value());

    std::optional<const ObjCLanguage::ObjCMethodName> lax_method =
        ObjCLanguage::ObjCMethodName::Create(name, /*strict = */ false);
    EXPECT_FALSE(lax_method.has_value());
  }
}
