/*
 * Copyright (c) 2017, 2024, Oracle and/or its affiliates.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 *
 * This program is designed to work with certain software (including
 * but not limited to OpenSSL) that is licensed under separate terms,
 * as designated in a particular file or component or in included license
 * documentation.  The authors of MySQL hereby grant you an additional
 * permission to link the program and your derivative works with the
 * separately licensed software that they have either included with
 * the program or referenced in the documentation.
 *
 * This program is distributed in the hope that it will be useful,  but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
 * the GNU General Public License, version 2.0, for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "mysqlshdk/libs/utils/version.h"

#include "unittest/gtest_clean.h"
#include "unittest/test_utils/mocks/gmock_clean.h"

namespace mysqlshdk {
namespace utils {
namespace {

using testing::ElementsAre;

TEST(Version, version_parsing) {
  // Full version
  {
    Version v("5.7.3-uno-dos");
    ASSERT_EQ(5, v.get_major());
    ASSERT_EQ(7, v.get_minor());
    ASSERT_EQ(3, v.get_patch());
    ASSERT_STREQ("uno-dos", v.get_extra().c_str());
    ASSERT_STREQ("5.7.3", v.get_base().c_str());
    ASSERT_STREQ("5.7.3-uno-dos", v.get_full().c_str());
  }

  // No patch
  {
    Version v("5.7-uno-dos");
    ASSERT_EQ(5, v.get_major());
    ASSERT_EQ(7, v.get_minor());
    ASSERT_EQ(0, v.get_patch());
    ASSERT_STREQ("uno-dos", v.get_extra().c_str());
    ASSERT_STREQ("5.7", v.get_base().c_str());
    ASSERT_STREQ("5.7-uno-dos", v.get_full().c_str());
  }

  // No patch and minor
  {
    Version v("5-uno-dos");
    ASSERT_EQ(5, v.get_major());
    ASSERT_EQ(0, v.get_minor());
    ASSERT_EQ(0, v.get_patch());
    ASSERT_STREQ("uno-dos", v.get_extra().c_str());
    ASSERT_STREQ("5", v.get_base().c_str());
    ASSERT_STREQ("5-uno-dos", v.get_full().c_str());
  }

  // No get_extra
  {
    Version v("5.7");
    ASSERT_EQ(5, v.get_major());
    ASSERT_EQ(7, v.get_minor());
    ASSERT_EQ(0, v.get_patch());
    ASSERT_STREQ("", v.get_extra().c_str());
    ASSERT_STREQ("5.7", v.get_base().c_str());
    ASSERT_STREQ("5.7", v.get_full().c_str());
  }

  // Comparisons
  {
    ASSERT_TRUE(Version("1.1.1") == Version("1.1.1"));
    ASSERT_TRUE(Version("1.1.0") == Version("1.1"));
    ASSERT_TRUE(Version("1.0.0") == Version("1"));

    ASSERT_FALSE(Version("1.0.0") == Version("1.0.1"));
    ASSERT_FALSE(Version("1.0.0") == Version("1.1.0"));
    ASSERT_FALSE(Version("1.0.0") == Version("2.0.0"));

    ASSERT_TRUE(Version("2.1.3") >= Version("2.1.3"));
    ASSERT_TRUE(Version("2.1.3") >= Version("2.1.2"));
    ASSERT_TRUE(Version("2.1.3") >= Version("2.1"));
    ASSERT_TRUE(Version("2.1.3") >= Version("2"));

    ASSERT_FALSE(Version("2.1.3") >= Version("2.1.4"));
    ASSERT_FALSE(Version("2.1.3") >= Version("2.2"));
    ASSERT_FALSE(Version("2.1.3") >= Version("3"));

    ASSERT_TRUE(Version("2.1.3") <= Version("2.1.3"));
    ASSERT_TRUE(Version("2.1.1") <= Version("2.1.2"));
    ASSERT_TRUE(Version("2.0") <= Version("2.1"));
    ASSERT_TRUE(Version("1.9.9") <= Version("2"));

    ASSERT_FALSE(Version("2.1.5") <= Version("2.1.4"));
    ASSERT_FALSE(Version("2.2.1") <= Version("2.2"));
    ASSERT_FALSE(Version("3.0.1") <= Version("3"));

    ASSERT_TRUE(Version("2.1.3") > Version("2.1.2"));
    ASSERT_TRUE(Version("2.1.3") > Version("2.1"));
    ASSERT_TRUE(Version("2.1.3") > Version("2"));

    ASSERT_FALSE(Version("2.1.3") > Version("2.1.3"));
    ASSERT_FALSE(Version("2.1.3") > Version("2.1.4"));
    ASSERT_FALSE(Version("2.1.3") > Version("2.2"));
    ASSERT_FALSE(Version("2.1.3") > Version("3"));

    ASSERT_TRUE(Version("2.1.1") < Version("2.1.2"));
    ASSERT_TRUE(Version("2.0") < Version("2.1"));
    ASSERT_TRUE(Version("1.9.9") < Version("2"));

    ASSERT_FALSE(Version("2.1.3") < Version("2.1.3"));
    ASSERT_FALSE(Version("2.1.5") < Version("2.1.4"));
    ASSERT_FALSE(Version("2.2.1") < Version("2.2"));
    ASSERT_FALSE(Version("3.0.1") < Version("3"));

    ASSERT_TRUE(Version("1.0.0") != Version("1.0.1"));
    ASSERT_TRUE(Version("1.0.0") != Version("1.1.0"));
    ASSERT_TRUE(Version("1.0.0") != Version("2.0.0"));

    ASSERT_FALSE(Version("1.1.1") != Version("1.1.1"));
    ASSERT_FALSE(Version("1.1.0") != Version("1.1"));
    ASSERT_FALSE(Version("1.0.0") != Version("1"));
  }

  ASSERT_ANY_THROW(Version("-uno-dos"));
  ASSERT_ANY_THROW(Version("5..2-uno-dos"));
  ASSERT_ANY_THROW(Version("5.2.-uno-dos"));
  ASSERT_ANY_THROW(Version("5.2.A-uno-dos"));

  EXPECT_EQ(80000, Version("8.0.0").numeric());
  EXPECT_EQ(10203, Version("1.2.3").numeric());
  EXPECT_EQ(12345, Version("1.23.45").numeric());
}

TEST(Version, corresponding_versions) {
  EXPECT_THAT(corresponding_versions(Version(8, 1, 0)),
              ElementsAre(Version(8, 0, 34), Version(8, 1, 0)));

  EXPECT_THAT(corresponding_versions(Version(8, 1, 1)),
              ElementsAre(Version(8, 0, 34), Version(8, 1, 1)));

  EXPECT_THAT(corresponding_versions(Version(8, 2, 0)),
              ElementsAre(Version(8, 0, 35), Version(8, 2, 0)));

  EXPECT_THAT(corresponding_versions(Version(8, 2, 1)),
              ElementsAre(Version(8, 0, 35), Version(8, 2, 1)));

  EXPECT_THAT(corresponding_versions(Version(8, 4, 0)),
              ElementsAre(Version(8, 0, 37), Version(8, 4, 0)));

  EXPECT_THAT(corresponding_versions(Version(8, 4, 1)),
              ElementsAre(Version(8, 0, 38), Version(8, 4, 1)));

  EXPECT_THAT(corresponding_versions(Version(8, 4, 2)),
              ElementsAre(Version(8, 0, 39), Version(8, 4, 2)));

  EXPECT_THAT(corresponding_versions(Version(8, 4, 3)),
              ElementsAre(Version(8, 0, 40), Version(8, 4, 3)));

  EXPECT_THAT(
      corresponding_versions(Version(9, 0, 0)),
      ElementsAre(Version(8, 0, 38), Version(8, 4, 1), Version(9, 0, 0)));

  EXPECT_THAT(
      corresponding_versions(Version(9, 0, 1)),
      ElementsAre(Version(8, 0, 39), Version(8, 4, 2), Version(9, 0, 1)));

  EXPECT_THAT(
      corresponding_versions(Version(9, 0, 2)),
      ElementsAre(Version(8, 0, 39), Version(8, 4, 2), Version(9, 0, 2)));

  EXPECT_THAT(
      corresponding_versions(Version(9, 1, 0)),
      ElementsAre(Version(8, 0, 40), Version(8, 4, 3), Version(9, 1, 0)));

  EXPECT_THAT(
      corresponding_versions(Version(9, 1, 1)),
      ElementsAre(Version(8, 0, 40), Version(8, 4, 3), Version(9, 1, 1)));

  EXPECT_THAT(
      corresponding_versions(Version(9, 7, 0)),
      ElementsAre(Version(8, 0, 46), Version(8, 4, 9), Version(9, 7, 0)));

  EXPECT_THAT(
      corresponding_versions(Version(9, 7, 1)),
      ElementsAre(Version(8, 0, 47), Version(8, 4, 10), Version(9, 7, 1)));

  EXPECT_THAT(corresponding_versions(Version(10, 0, 0)),
              ElementsAre(Version(8, 0, 47), Version(8, 4, 10),
                          Version(9, 7, 1), Version(10, 0, 0)));

  EXPECT_THAT(corresponding_versions(Version(10, 0, 1)),
              ElementsAre(Version(8, 0, 47), Version(8, 4, 10),
                          Version(9, 7, 1), Version(10, 0, 1)));

  EXPECT_THAT(corresponding_versions(Version(10, 7, 0)),
              ElementsAre(Version(8, 0, 54), Version(8, 4, 17),
                          Version(9, 7, 8), Version(10, 7, 0)));

  EXPECT_THAT(
      corresponding_versions(Version(11, 0, 0)),
      ElementsAre(Version(8, 0, 55), Version(8, 4, 18), Version(9, 7, 9),
                  Version(10, 7, 1), Version(11, 0, 0)));
}

}  // namespace
}  // namespace utils
}  // namespace mysqlshdk
