diff --git a/libmamba/include/mamba/specs/match_spec.hpp b/libmamba/include/mamba/specs/match_spec.hpp index 2f08d43dd..1d4ceaa1e 100644 --- a/libmamba/include/mamba/specs/match_spec.hpp +++ b/libmamba/include/mamba/specs/match_spec.hpp @@ -153,10 +153,7 @@ namespace mamba::specs return !(*this == other); } - auto extra_members_hash() const -> std::size_t - { - return mamba::util::hash_vals(m_extra); - } + auto extra_members_hash() const -> std::size_t; private: diff --git a/libmamba/include/mamba/specs/regex_spec.hpp b/libmamba/include/mamba/specs/regex_spec.hpp index 3c8b41d0b..74edbbecc 100644 --- a/libmamba/include/mamba/specs/regex_spec.hpp +++ b/libmamba/include/mamba/specs/regex_spec.hpp @@ -75,4 +75,13 @@ struct fmt::formatter format(const ::mamba::specs::RegexSpec& spec, format_context& ctx) const -> decltype(ctx.out()); }; +template <> +struct std::hash +{ + auto operator()(const mamba::specs::RegexSpec& spec) const -> std::size_t + { + return std::hash{}(spec.str()); + } +}; + #endif diff --git a/libmamba/include/mamba/util/flat_set.hpp b/libmamba/include/mamba/util/flat_set.hpp index 8796edd90..8275a3b3b 100644 --- a/libmamba/include/mamba/util/flat_set.hpp +++ b/libmamba/include/mamba/util/flat_set.hpp @@ -565,7 +565,7 @@ struct std::hash> { auto operator()(const mamba::util::flat_set& set) const -> std::size_t { - return mamba::util::hash_vals(set); + return mamba::util::hash_range(set); } }; diff --git a/libmamba/include/mamba/util/heap_optional.hpp b/libmamba/include/mamba/util/heap_optional.hpp index 2ff7e06e9..ae53f82f1 100644 --- a/libmamba/include/mamba/util/heap_optional.hpp +++ b/libmamba/include/mamba/util/heap_optional.hpp @@ -251,17 +251,4 @@ namespace mamba::util } } -template -struct std::hash> -{ - std::size_t operator()(const mamba::util::heap_optional& opt) const - { - if (opt.has_value()) - { - return std::hash{}(*opt); - } - return 0; - } -}; - #endif diff --git a/libmamba/src/specs/match_spec.cpp b/libmamba/src/specs/match_spec.cpp index 0af970dbc..405eb447c 100644 --- a/libmamba/src/specs/match_spec.cpp +++ b/libmamba/src/specs/match_spec.cpp @@ -1044,6 +1044,11 @@ namespace mamba::specs return *m_extra; } + auto MatchSpec::extra_members_hash() const -> std::size_t + { + return std::hash{}(m_extra.value_or(ExtraMembers())); + } + namespace match_spec_literals { auto operator""_ms(const char* str, std::size_t len) -> MatchSpec diff --git a/libmamba/tests/src/specs/test_build_number_spec.cpp b/libmamba/tests/src/specs/test_build_number_spec.cpp index 26295da71..98b8fa011 100644 --- a/libmamba/tests/src/specs/test_build_number_spec.cpp +++ b/libmamba/tests/src/specs/test_build_number_spec.cpp @@ -137,4 +137,22 @@ TEST_SUITE("specs::build_number_spec") CHECK_FALSE(BuildNumberSpec::parse("=3").value().is_explicitly_free()); CHECK_FALSE(BuildNumberSpec::parse("<2").value().is_explicitly_free()); } + + TEST_CASE("Comparability and hashability") + { + auto bn1 = BuildNumberSpec::parse("=3").value(); + auto bn2 = BuildNumberSpec::parse("3").value(); + auto bn3 = BuildNumberSpec::parse("*").value(); + + CHECK_EQ(bn1, bn2); + CHECK_NE(bn1, bn3); + + auto hash_fn = std::hash{}; + auto bn1_hash = hash_fn(bn1); + auto bn2_hash = hash_fn(bn2); + auto bn3_hash = hash_fn(bn3); + + CHECK_EQ(bn1_hash, bn2_hash); + CHECK_NE(bn1_hash, bn3_hash); + } } diff --git a/libmamba/tests/src/specs/test_chimera_string_spec.cpp b/libmamba/tests/src/specs/test_chimera_string_spec.cpp index 4a91039cb..caf437b1a 100644 --- a/libmamba/tests/src/specs/test_chimera_string_spec.cpp +++ b/libmamba/tests/src/specs/test_chimera_string_spec.cpp @@ -70,4 +70,18 @@ TEST_SUITE("specs::chimera_string_spec") CHECK_FALSE(spec.is_exact()); CHECK_FALSE(spec.is_glob()); } + + TEST_CASE("Comparability and hashability") + { + auto spec1 = ChimeraStringSpec::parse("mkl").value(); + auto spec2 = ChimeraStringSpec::parse("mkl").value(); + auto spec3 = ChimeraStringSpec::parse("*").value(); + + CHECK_EQ(spec1, spec2); + CHECK_NE(spec1, spec3); + + std::hash hash_fn; + CHECK_EQ(hash_fn(spec1), hash_fn(spec2)); + CHECK_NE(hash_fn(spec1), hash_fn(spec3)); + } } diff --git a/libmamba/tests/src/specs/test_glob_spec.cpp b/libmamba/tests/src/specs/test_glob_spec.cpp index 461fb2a25..0d99a8ae4 100644 --- a/libmamba/tests/src/specs/test_glob_spec.cpp +++ b/libmamba/tests/src/specs/test_glob_spec.cpp @@ -55,4 +55,18 @@ TEST_SUITE("specs::glob_spec") CHECK_FALSE(spec.is_free()); CHECK_FALSE(spec.is_exact()); } + + TEST_CASE("Comparability and hashability") + { + auto spec1 = GlobSpec("py*"); + auto spec2 = GlobSpec("py*"); + auto spec3 = GlobSpec("pyth*"); + + CHECK_EQ(spec1, spec2); + CHECK_NE(spec1, spec3); + + auto hash_fn = std::hash(); + CHECK_EQ(hash_fn(spec1), hash_fn(spec2)); + CHECK_NE(hash_fn(spec1), hash_fn(spec3)); + } } diff --git a/libmamba/tests/src/specs/test_match_spec.cpp b/libmamba/tests/src/specs/test_match_spec.cpp index 4a83d7a75..63b76d36b 100644 --- a/libmamba/tests/src/specs/test_match_spec.cpp +++ b/libmamba/tests/src/specs/test_match_spec.cpp @@ -812,4 +812,31 @@ TEST_SUITE("specs::match_spec") })); } } + + TEST_CASE("MatchSpec comparability and hashability") + { + using namespace specs::match_spec_literals; + using namespace specs::version_literals; + + const auto spec1 = "py*>=3.7=bld[build_number='<=2', md5=lemd5, track_features='mkl,openssl']"_ms; + + // Create an identical specification + const auto spec2 = "py*>=3.7=bld[build_number='<=2', md5=lemd5, track_features='mkl,openssl']"_ms; + + // Create a different specification + const auto spec3 = "py*>=3.7=bld[build_number='<=2', md5=lemd5, track_features='mkl']"_ms; + + // Check that the two copies are equal + CHECK_EQ(spec1, spec2); + // Check that the different specification is not equal to the first one + CHECK_NE(spec1, spec3); + + // Check that the hash of the two copies is the same + auto spec1_hash = std::hash{}(spec1); + auto spec2_hash = std::hash{}(spec2); + auto spec3_hash = std::hash{}(spec3); + + CHECK_EQ(spec1_hash, spec2_hash); + CHECK_NE(spec1_hash, spec3_hash); + } } diff --git a/libmamba/tests/src/specs/test_package_info.cpp b/libmamba/tests/src/specs/test_package_info.cpp index 87a306aab..1a321709f 100644 --- a/libmamba/tests/src/specs/test_package_info.cpp +++ b/libmamba/tests/src/specs/test_package_info.cpp @@ -190,5 +190,39 @@ TEST_SUITE("specs::package_info") CHECK_FALSE(j.get() != pkg); } } + + SUBCASE("PackageInfo comparability and hashability") + { + auto pkg2 = PackageInfo(); + pkg2.name = "foo"; + pkg2.version = "4.0"; + pkg2.build_string = "mybld"; + pkg2.build_number = 5; + pkg2.noarch = NoArchType::Generic; + pkg2.channel = "conda-forge"; + pkg2.package_url = "https://repo.mamba.pm/conda-forge/linux-64/foo-4.0-mybld.conda"; + pkg2.platform = "linux-64"; + pkg2.filename = "foo-4.0-mybld.conda"; + pkg2.license = "MIT"; + pkg2.size = 3200; + pkg2.timestamp = 4532; + pkg2.sha256 = "01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b"; + pkg2.signatures = R"("signatures": { "some_file.tar.bz2": { "a133184c9c7a651f55db194031a6c1240b798333923dc9319d1fe2c94a1242d": { "signature": "7a67a875d0454c14671d960a02858e059d154876dab6b3873304a27102063c9c25"}}})"; + pkg2.md5 = "68b329da9893e34099c7d8ad5cb9c940"; + pkg2.track_features = { "mkl", "blas" }; + pkg2.dependencies = { "python>=3.7", "requests" }; + pkg2.constrains = { "pip>=2.1" }; + + auto hash_fn = std::hash{}; + + CHECK_EQ(pkg, pkg2); + CHECK_EQ(hash_fn(pkg), hash_fn(pkg2)); + + + pkg2.md5[0] = '0'; + + CHECK_NE(pkg, pkg2); + CHECK_NE(hash_fn(pkg), hash_fn(pkg2)); + } } } diff --git a/libmamba/tests/src/specs/test_regex_spec.cpp b/libmamba/tests/src/specs/test_regex_spec.cpp index f6dec9944..878551921 100644 --- a/libmamba/tests/src/specs/test_regex_spec.cpp +++ b/libmamba/tests/src/specs/test_regex_spec.cpp @@ -66,4 +66,18 @@ TEST_SUITE("specs::regex_spec") CHECK_FALSE(spec.is_explicitly_free()); CHECK_FALSE(spec.is_exact()); } + + TEST_CASE("Comparability and hashability") + { + auto spec1 = RegexSpec::parse("pyth*").value(); + auto spec2 = RegexSpec::parse("pyth*").value(); + auto spec3 = RegexSpec::parse("python").value(); + + CHECK_EQ(spec1, spec2); + CHECK_NE(spec1, spec3); + + auto hash_fn = std::hash(); + CHECK_EQ(hash_fn(spec1), hash_fn(spec2)); + CHECK_NE(hash_fn(spec1), hash_fn(spec3)); + } } diff --git a/libmamba/tests/src/specs/test_unresolved_channel.cpp b/libmamba/tests/src/specs/test_unresolved_channel.cpp index b4b3bceb4..1a88162ef 100644 --- a/libmamba/tests/src/specs/test_unresolved_channel.cpp +++ b/libmamba/tests/src/specs/test_unresolved_channel.cpp @@ -242,4 +242,18 @@ TEST_SUITE("specs::unresolved_channel") "location[linux-64,noarch]" ); } + + TEST_CASE("Comparability and hashability") + { + auto uc1 = UnresolvedChannel::parse("conda-forge").value(); + auto uc2 = UnresolvedChannel::parse("conda-forge").value(); + auto uc3 = UnresolvedChannel::parse("conda-forge/linux-64").value(); + + CHECK_EQ(uc1, uc2); + CHECK_NE(uc1, uc3); + + auto hash_fn = std::hash(); + CHECK_EQ(hash_fn(uc1), hash_fn(uc2)); + CHECK_NE(hash_fn(uc1), hash_fn(uc3)); + } } diff --git a/libmamba/tests/src/specs/test_version_spec.cpp b/libmamba/tests/src/specs/test_version_spec.cpp index fb5c097ef..6b80fdaa7 100644 --- a/libmamba/tests/src/specs/test_version_spec.cpp +++ b/libmamba/tests/src/specs/test_version_spec.cpp @@ -444,4 +444,19 @@ TEST_SUITE("specs::version_spec") CHECK_FALSE(VersionSpec::parse("==2.3|!=2.3").value().is_explicitly_free()); CHECK_FALSE(VersionSpec::parse("=2.3,<3.0").value().is_explicitly_free()); } + + + TEST_CASE("Comparability and hashability") + { + auto spec1 = VersionSpec::parse("*").value(); + auto spec2 = VersionSpec::parse("*").value(); + auto spec3 = VersionSpec::parse("=2.4").value(); + + CHECK_EQ(spec1, spec2); + CHECK_NE(spec1, spec3); + + auto hash_fn = std::hash(); + CHECK_EQ(hash_fn(spec1), hash_fn(spec2)); + CHECK_NE(hash_fn(spec1), hash_fn(spec3)); + } }