/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdexcept>

#include <glog/logging.h>

#include <folly/portability/GMock.h>
#include <folly/portability/GTest.h>
#include <folly/test/JsonMockUtil.h>
#include <folly/test/JsonTestUtil.h>

using namespace folly;

TEST(CompareJson, Simple) {
  constexpr StringPiece json1 = R"({"a": 1, "b": 2})";
  constexpr StringPiece json2 = R"({"b": 2, "a": 1})";
  EXPECT_TRUE(compareJson(json1, json2));
  FOLLY_EXPECT_JSON_EQ(json1, json2);

  constexpr StringPiece json3 = R"({"b": 3, "a": 1})";
  EXPECT_FALSE(compareJson(json1, json3));
}

TEST(CompareJson, Nested) {
  constexpr StringPiece json1 = R"({"a": "{\"A\": 1, \"B\": 2}", "b": 2})";
  LOG(INFO) << json1 << "\n";
  constexpr StringPiece json2 = R"({"b": 2, "a": "{\"B\": 2, \"A\": 1}"})";
  EXPECT_FALSE(compareJsonWithNestedJson(json1, json2, 0));
  EXPECT_TRUE(compareJsonWithNestedJson(json1, json2, 1));

  constexpr StringPiece json3 = R"({"b": 2, "a": "{\"B\": 3, \"A\": 1}"})";
  EXPECT_FALSE(compareJsonWithNestedJson(json1, json3, 0));
  EXPECT_FALSE(compareJsonWithNestedJson(json1, json3, 1));
}

TEST(CompareJson, Malformed) {
  constexpr StringPiece json1 = R"({"a": 1, "b": 2})";
  constexpr StringPiece json2 = R"({"b": 2, "a": 1)";
  EXPECT_THROW(compareJson(json1, json2), std::runtime_error);
}

TEST(CompareJsonWithTolerance, Simple) {
  // Use the same tolerance for all tests.
  auto compare = [](StringPiece obj1, StringPiece obj2) {
    return compareJsonWithTolerance(obj1, obj2, 0.1);
  };

  EXPECT_TRUE(compare("1", "1.05"));
  EXPECT_FALSE(compare("1", "1.2"));

  EXPECT_TRUE(compare("true", "true"));
  EXPECT_FALSE(compare("true", "false"));
  EXPECT_FALSE(compare("true", "1"));

  EXPECT_TRUE(compare(R"([1, 2, 3])", R"([1.05, 2, 3.01])"));
  EXPECT_FALSE(compare(R"([1, 2, 3])", R"([1.2, 2, 3.01])"));
  EXPECT_FALSE(compare(R"([1, 2, 3])", R"([1, 2])"));

  EXPECT_TRUE(compare("1.0", "1.05"));
  EXPECT_FALSE(compare("1.0", "1.2"));

  EXPECT_TRUE(compare("1", "1"));
  EXPECT_FALSE(compare("1", "2"));

  EXPECT_TRUE(compare(R"({"a": 1, "b": 2})", R"({"b": 2.01, "a": 1.05})"));
  EXPECT_FALSE(compare(R"({"a": 1, "b": 2})", R"({"b": 2.01, "a": 1.2})"));
  EXPECT_FALSE(compare(R"({"a": 1, "b": 2})", R"({"b": 2})"));
  EXPECT_FALSE(compare(R"({"a": 1, "b": 2})", R"({"c": 2.01, "a": 1.05})"));

  EXPECT_TRUE(compare(R"("hello")", R"("hello")"));
  EXPECT_FALSE(compare(R"("hello")", R"("world")"));

  FOLLY_EXPECT_JSON_NEAR(
      R"({"a": 1, "b": 2})", R"({"b": 2.01, "a": 1.05})", 0.1);
}

TEST(JsonEqMock, Simple) {
  EXPECT_THAT(StringPiece{"1"}, JsonEq<StringPiece>("1"));
  EXPECT_THAT(std::string{"1"}, JsonEq<std::string>("1"));
  EXPECT_THAT(std::string{"1"}, JsonEq<std::string const&>("1"));
  EXPECT_THAT(std::string{"^J1"}, JsonEq<std::string const&>("1", "^J"));
}
