/* * Copyright (C) 2021 Emeric Poupon * * This file is part of LMS. * * LMS 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, either version 3 of the License, or * (at your option) any later version. * * LMS 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 for more details. * * You should have received a copy of the GNU General Public License * along with LMS. If not, see . */ #include "Common.hpp" #include #include using namespace Database; TEST_F(DatabaseFixture, Cluster) { { auto transaction{ session.createWriteTransaction() }; EXPECT_EQ(Cluster::getCount(session), 0); EXPECT_EQ(ClusterType::getCount(session), 0); } ScopedClusterType clusterType{ session, "MyType" }; { auto transaction{ session.createWriteTransaction() }; EXPECT_EQ(ClusterType::getCount(session), 1); } { ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { auto transaction{ session.createWriteTransaction() }; EXPECT_EQ(Cluster::getCount(session), 1); EXPECT_EQ(cluster->getType()->getId(), clusterType.getId()); { const auto clusters{ Cluster::findIds(session, Cluster::FindParameters {}) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster.getId()); } { const auto clusters{ Cluster::findOrphanIds(session) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster.getId()); } auto clusterTypes{ ClusterType::findIds(session) }; ASSERT_EQ(clusterTypes.results.size(), 1); EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); clusterTypes = ClusterType::findUsed(session); ASSERT_EQ(clusterTypes.results.size(), 1); EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); clusterTypes = ClusterType::findOrphanIds(session); EXPECT_TRUE(clusterTypes.results.empty()); } } { auto transaction{ session.createWriteTransaction() }; auto clusterTypes{ ClusterType::findOrphanIds(session) }; ASSERT_EQ(clusterTypes.results.size(), 1); EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); ASSERT_TRUE(ClusterType::findUsed(session).results.empty()); } } TEST_F(DatabaseFixture, Cluster_singleTrack) { ScopedTrack track{ session, "MyTrack" }; ScopedClusterType clusterType{ session, "MyClusterType" }; { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); auto clusterTypes{ ClusterType::findOrphanIds(session) }; ASSERT_EQ(clusterTypes.results.size(), 1); EXPECT_EQ(clusterTypes.results.front(), clusterType.getId()); } ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { auto transaction{ session.createReadTransaction() }; auto clusters{ Cluster::findOrphanIds(session) }; EXPECT_EQ(clusters.results.size(), 2); EXPECT_TRUE(track->getClusters().empty()); EXPECT_TRUE(track->getClusterIds().empty()); EXPECT_EQ(Cluster::computeTrackCount(session, cluster1.getId()), 0); EXPECT_EQ(Cluster::computeTrackCount(session, cluster2.getId()), 0); } { auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; auto clusters{ Cluster::findIds(session, Cluster::FindParameters {}.setTrack(track.getId())) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster1.getId()); EXPECT_EQ(Cluster::computeTrackCount(session, cluster1.getId()), 1); EXPECT_EQ(Cluster::computeTrackCount(session, cluster2.getId()), 0); } { auto transaction{ session.createReadTransaction() }; auto clusters{ Cluster::findOrphanIds(session) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster2.getId()); EXPECT_TRUE(ClusterType::findOrphanIds(session).results.empty()); } { auto transaction{ session.createReadTransaction() }; auto tracks{ Track::findIds(session, Track::FindParameters {}.setClusters({cluster1.getId()})) }; ASSERT_EQ(tracks.results.size(), 1); EXPECT_EQ(tracks.results.front(), track.getId()); tracks = Track::findIds(session, Track::FindParameters{}.setClusters({ cluster2.getId() })); EXPECT_TRUE(tracks.results.empty()); } { auto transaction{ session.createReadTransaction() }; auto clusters{ track->getClusters() }; ASSERT_EQ(clusters.size(), 1); EXPECT_EQ(clusters.front()->getId(), cluster1.getId()); auto clusterIds{ track->getClusterIds() }; ASSERT_EQ(clusterIds.size(), 1); EXPECT_EQ(clusterIds.front(), cluster1.getId()); } } TEST_F(DatabaseFixture, Cluster_singleTrackWithSeveralClusters) { ScopedTrack track{ session, "MyTrack" }; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; const std::vector clusterIds{ cluster1.getId(), cluster2.getId() }; { auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters{}.setClusters(clusterIds)) }; EXPECT_TRUE(tracks.results.empty()); } { auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters{}.setClusters(clusterIds)) }; EXPECT_TRUE(tracks.results.empty()); EXPECT_EQ(Cluster::computeTrackCount(session, cluster1.getId()), 1); EXPECT_EQ(Cluster::computeTrackCount(session, cluster2.getId()), 0); } { auto transaction{ session.createWriteTransaction() }; cluster2.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; const auto tracks{ Track::findIds(session, Track::FindParameters{}.setClusters(clusterIds)) }; ASSERT_FALSE(tracks.results.empty()); EXPECT_EQ(tracks.results.front(), track.getId()); EXPECT_EQ(Cluster::computeTrackCount(session, cluster1.getId()), 1); EXPECT_EQ(Cluster::computeTrackCount(session, cluster2.getId()), 1); } } TEST_F(DatabaseFixture, Cluster_multiTracks) { std::list tracks; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; for (std::size_t i{}; i < 10; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(tracks.back().get()); } } { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_EQ(Cluster::computeTrackCount(session, cluster.getId()), tracks.size()); for (TrackId trackId : cluster->getTracks().results) { auto it{ std::find_if(std::cbegin(tracks), std::cend(tracks), [&](const ScopedTrack& track) { return trackId == track.getId(); }) }; EXPECT_TRUE(it != std::cend(tracks)); } } } TEST_F(DatabaseFixture, ClusterType_singleTrack) { { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::find(session, Cluster::FindParameters{}).results.empty()); EXPECT_TRUE(Cluster::find(session, Cluster::FindParameters{}.setClusterTypeName("Foo")).results.empty()); } ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { auto transaction{ session.createReadTransaction() }; auto clusters {Cluster::findIds(session, Cluster::FindParameters{}).results}; ASSERT_EQ(clusters.size(), 1); EXPECT_EQ(clusters.front(), cluster.getId()); clusters = Cluster::findIds(session, Cluster::FindParameters{}.setClusterType(clusterType.getId())).results; ASSERT_EQ(clusters.size(), 1); EXPECT_EQ(clusters.front(), cluster.getId()); clusters = Cluster::findIds(session, Cluster::FindParameters{}.setClusterTypeName("Foo")).results; EXPECT_EQ(clusters.size(), 0); clusters = Cluster::findIds(session, Cluster::FindParameters{}.setClusterTypeName("MyClusterType")).results; ASSERT_EQ(clusters.size(), 1); EXPECT_EQ(clusters.front(), cluster.getId()); } } TEST_F(DatabaseFixture, Cluster_singleTrackSingleReleaseSingleCluster) { ScopedTrack track{ session, "MyTrackFile" }; ScopedRelease release{ session, "MyRelease" }; { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); } ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; ScopedCluster unusedCluster{ session, clusterType.lockAndGet(), "MyClusterUnused" }; { auto transaction{ session.createReadTransaction() }; ASSERT_EQ(Cluster::findOrphanIds(session).results.size(), 2); EXPECT_TRUE(Release::find(session, Release::FindParameters{}.setClusters({ unusedCluster.getId() })).results.empty()); EXPECT_EQ(Release::find(session, Release::FindParameters{}).results.size(), 1); EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 0); EXPECT_EQ(Cluster::computeReleaseCount(session, unusedCluster.getId()), 0); } { auto transaction{ session.createWriteTransaction() }; track.get().modify()->setRelease(release.get()); cluster.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; { auto clusters{ Cluster::findOrphanIds(session) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), unusedCluster.getId()); } EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 1); EXPECT_EQ(Cluster::computeReleaseCount(session, unusedCluster.getId()), 0); } { auto transaction{ session.createReadTransaction() }; const auto clusters{ Cluster::findIds(session, Cluster::FindParameters{}.setRelease(release.getId())) }; ASSERT_EQ(clusters.results.size(), 1); EXPECT_EQ(clusters.results.front(), cluster.getId()); } { auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(releases.results.size(), 1); EXPECT_EQ(releases.results.front(), release.getId()); } { auto transaction{ session.createReadTransaction() }; const auto releases{ Release::findIds(session, Release::FindParameters {}.setClusters({unusedCluster.getId()})) }; EXPECT_EQ(releases.results.size(), 0); } { auto transaction{ session.createReadTransaction() }; EXPECT_EQ(Cluster::computeReleaseCount(session, cluster.getId()), 1); EXPECT_EQ(Cluster::computeTrackCount(session, cluster.getId()), 1); EXPECT_EQ(Cluster::computeReleaseCount(session, unusedCluster.getId()), 0); EXPECT_EQ(Cluster::computeTrackCount(session, unusedCluster.getId()), 0); } } TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiClusters) { ScopedTrack track{ session, "MyTrackFile" }; ScopedArtist artist{ session, "MyArtist" }; ScopedClusterType clusterType{ session, "MyType" }; ScopedCluster cluster1{ session, clusterType.lockAndGet(), "Cluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "Cluster2" }; ScopedCluster cluster3{ session, clusterType.lockAndGet(), "Cluster3" }; { auto transaction{ session.createWriteTransaction() }; auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; cluster1.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(ClusterType::findOrphanIds(session).results.empty()); EXPECT_EQ(Cluster::findOrphanIds(session).results.size(), 2); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { auto transaction{ session.createReadTransaction() }; EXPECT_EQ(track->getClusters().size(), 1); EXPECT_EQ(track->getClusterIds().size(), 1); } { auto transaction{ session.createWriteTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); EXPECT_TRUE(Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster2.getId() })).results.empty()); EXPECT_TRUE(Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.empty()); cluster2.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster1.getId()})) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); artists = Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster2.getId() })); ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); artists = Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster1.getId() })); ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); EXPECT_TRUE(Artist::findIds(session, Artist::FindParameters{}.setClusters({ cluster3.getId() })).results.empty()); } } TEST_F(DatabaseFixture, SingleTrackSingleArtistMultiRolesMultiClusters) { ScopedTrack track{ session, "MyTrackFile" }; ScopedArtist artist{ session, "MyArtist" }; ScopedClusterType clusterType{ session, "MyType" }; ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::ReleaseArtist); cluster.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } } TEST_F(DatabaseFixture, MultiTracksSingleArtistMultiClusters) { constexpr std::size_t nbTracks{ 10 }; constexpr std::size_t nbClusters{ 5 }; std::list tracks; std::list clusters; ScopedArtist artist{ session, "MyArtist" }; ScopedClusterType clusterType{ session, "MyType" }; for (std::size_t i{}; i < nbClusters; ++i) clusters.emplace_back(session, clusterType.lockAndGet(), "MyCluster" + std::to_string(i)); for (std::size_t i{}; i < nbTracks; ++i) { tracks.emplace_back(session, "MyTrackFile" + std::to_string(i)); auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist); for (auto& cluster : clusters) cluster.get().modify()->addTrack(tracks.back().get()); } { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); } { auto transaction{ session.createReadTransaction() }; std::vector clusterIds; std::transform(std::cbegin(clusters), std::cend(clusters), std::back_inserter(clusterIds), [](const ScopedCluster& cluster) { return cluster.getId(); }); auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters(clusterIds)) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); } } TEST_F(DatabaseFixture, MultipleTracksSingleClusterSimilarity) { std::list tracks; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyClusterType" }; for (std::size_t i{}; i < 10; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { auto transaction{ session.createWriteTransaction() }; cluster.get().modify()->addTrack(tracks.back().get()); } } { auto transaction{ session.createReadTransaction() }; const auto similarTracks{ Track::findSimilarTrackIds(session, {tracks.front().getId()}) }; EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); for (const TrackId similarTrackId : similarTracks.results) { EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); } } } TEST_F(DatabaseFixture, MultipleTracksMultipleClustersSimilarity) { std::list tracks; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; for (std::size_t i{}; i < 5; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(tracks.back().get()); } } for (std::size_t i{ 5 }; i < 10; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); { auto transaction{ session.createWriteTransaction() }; cluster1.get().modify()->addTrack(tracks.back().get()); cluster2.get().modify()->addTrack(tracks.back().get()); } } { auto transaction{ session.createReadTransaction() }; { auto similarTracks{ Track::findSimilarTrackIds(session, {tracks.back().getId()}, Range {0, 4}) }; EXPECT_EQ(similarTracks.results.size(), 4); for (const TrackId similarTrackId : similarTracks.results) EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 5), std::next(std::cend(tracks), -1), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); } { auto similarTracks{ Track::findSimilarTrackIds(session, {tracks.front().getId()}) }; EXPECT_EQ(similarTracks.results.size(), tracks.size() - 1); for (const TrackId similarTrackId : similarTracks.results) EXPECT_TRUE(std::find_if(std::next(std::cbegin(tracks), 1), std::cend(tracks), [&](const auto& track) { return similarTrackId == track.getId(); }) != std::cend(tracks)); } } } TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistSingleCluster) { ScopedTrack track{ session, "MyTrack" }; ScopedRelease release{ session, "MyRelease" }; ScopedArtist artist{ session, "MyArtist" }; ScopedClusterType clusterType{ session, "MyType" }; ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; { auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist); track.get().modify()->setRelease(release.get()); cluster.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(Cluster::findOrphanIds(session).results.empty()); EXPECT_TRUE(ClusterType::findOrphanIds(session).results.empty()); EXPECT_TRUE(Artist::findOrphanIds(session).results.empty()); EXPECT_TRUE(Release::findOrphanIds(session).results.empty()); } { auto transaction{ session.createReadTransaction() }; auto artists{ Artist::findIds(session, Artist::FindParameters {}.setClusters({cluster.getId()})) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist.getId()); auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) }; ASSERT_EQ(releases.results.size(), 1); EXPECT_EQ(releases.results.front(), release.getId()); releases = Release::findIds(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters({ cluster.getId() })); ASSERT_EQ(releases.results.size(), 1); EXPECT_EQ(releases.results.front(), release.getId()); } } TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtistMultiClusters) { ScopedTrack track{ session, "MyTrack" }; ScopedRelease release{ session, "MyRelease" }; ScopedArtist artist{ session, "MyArtist" }; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { auto transaction{ session.createWriteTransaction() }; auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) }; track.get().modify()->setRelease(release.get()); cluster1.get().modify()->addTrack(track.get()); cluster2.get().modify()->addTrack(track.get()); } { auto transaction{ session.createReadTransaction() }; auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) }; ASSERT_EQ(releases.results.size(), 1); EXPECT_EQ(releases.results.front(), release.getId()); releases = Release::findIds(session, Release::FindParameters{}.setArtist(artist.getId()).setClusters({ cluster1.getId(), cluster2.getId() })); ASSERT_EQ(releases.results.size(), 1); EXPECT_EQ(releases.results.front(), release.getId()); } } TEST_F(DatabaseFixture, SingleTrackListMultipleTrackSingleCluster) { ScopedUser user{ session, "MyUser" }; ScopedTrackList trackList{ session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet() }; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" }; std::list tracks; for (std::size_t i{}; i < 20; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); auto transaction{ session.createWriteTransaction() }; if (i < 5) session.create(tracks.back().get(), trackList.get()); if (i < 10) cluster.get().modify()->addTrack(tracks.back().get()); } { auto transaction{ session.createReadTransaction() }; const auto similarTracks{ trackList->getSimilarTracks() }; EXPECT_EQ(similarTracks.size(), 5); for (auto similarTrack : similarTracks) EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::cend(tracks), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); } } TEST_F(DatabaseFixture, SingleTrackListMultipleTrackMultiClusters) { ScopedUser user{ session, "MyUser" }; ScopedTrackList trackList{ session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet() }; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; std::list tracks; for (std::size_t i{}; i < 20; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); auto transaction{ session.createWriteTransaction() }; if (i < 5) session.create(tracks.back().get(), trackList.get()); if (i < 10) { cluster1.get().modify()->addTrack(tracks.back().get()); cluster2.get().modify()->addTrack(tracks.back().get()); } else if (i < 15) { cluster1.get().modify()->addTrack(tracks.back().get()); } } { auto transaction{ session.createReadTransaction() }; { const auto similarTracks{ trackList->getSimilarTracks(0, 5) }; ASSERT_EQ(similarTracks.size(), 5); for (auto similarTrack : similarTracks) EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 5), std::next(std::cbegin(tracks), 10), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); } { const auto similarTracks{ trackList->getSimilarTracks(5, 10) }; ASSERT_EQ(similarTracks.size(), 5); for (auto similarTrack : similarTracks) EXPECT_TRUE(std::any_of(std::next(std::cbegin(tracks), 10), std::next(std::cbegin(tracks), 15), [similarTrack](const ScopedTrack& track) { return track.getId() == similarTrack->getId(); })); } EXPECT_TRUE(trackList->getSimilarTracks(10, 10).empty()); } } TEST_F(DatabaseFixture, MultipleTracksMultipleArtistsMultiClusters) { ScopedArtist artist1{ session, "MyArtist1" }; ScopedArtist artist2{ session, "MyArtist2" }; ScopedArtist artist3{ session, "MyArtist3" }; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(artist1->findSimilarArtistIds().results.empty()); EXPECT_TRUE(artist2->findSimilarArtistIds().results.empty()); EXPECT_TRUE(artist3->findSimilarArtistIds().results.empty()); } std::list tracks; for (std::size_t i{}; i < 10; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); auto transaction{ session.createWriteTransaction() }; if (i < 5) TrackArtistLink::create(session, tracks.back().get(), artist1.get(), TrackArtistLinkType::Artist); else { TrackArtistLink::create(session, tracks.back().get(), artist2.get(), TrackArtistLinkType::Artist); cluster2.get().modify()->addTrack(tracks.back().get()); } cluster1.get().modify()->addTrack(tracks.back().get()); } tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); { auto transaction{ session.createWriteTransaction() }; TrackArtistLink::create(session, tracks.back().get(), artist3.get(), TrackArtistLinkType::Artist); cluster2.get().modify()->addTrack(tracks.back().get()); } { auto transaction{ session.createReadTransaction() }; { auto artists{ artist1->findSimilarArtistIds() }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist2.getId()); } { auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::Artist}) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist2.getId()); } { auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::ReleaseArtist}) }; EXPECT_EQ(artists.results.empty(), 1); } { auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::Artist, TrackArtistLinkType::ReleaseArtist}) }; ASSERT_EQ(artists.results.size(), 1); EXPECT_EQ(artists.results.front(), artist2.getId()); } { auto artists{ artist1->findSimilarArtistIds({TrackArtistLinkType::Composer}) }; EXPECT_TRUE(artists.results.empty()); } { auto artists{ artist2->findSimilarArtistIds() }; ASSERT_EQ(artists.results.size(), 2); EXPECT_EQ(artists.results[0], artist1.getId()); EXPECT_EQ(artists.results[1], artist3.getId()); } } } TEST_F(DatabaseFixture, MultipleTracksMultipleReleasesMultiClusters) { ScopedRelease release1{ session, "MyRelease1" }; ScopedRelease release2{ session, "MyRelease2" }; ScopedRelease release3{ session, "MyRelease3" }; ScopedClusterType clusterType{ session, "MyClusterType" }; ScopedCluster cluster1{ session, clusterType.lockAndGet(), "MyCluster1" }; ScopedCluster cluster2{ session, clusterType.lockAndGet(), "MyCluster2" }; { auto transaction{ session.createReadTransaction() }; EXPECT_TRUE(release1->getSimilarReleases().empty()); EXPECT_TRUE(release2->getSimilarReleases().empty()); EXPECT_TRUE(release3->getSimilarReleases().empty()); } std::list tracks; for (std::size_t i{}; i < 10; ++i) { tracks.emplace_back(session, "MyTrack" + std::to_string(i)); auto transaction{ session.createWriteTransaction() }; if (i < 5) tracks.back().get().modify()->setRelease(release1.get()); else { tracks.back().get().modify()->setRelease(release2.get()); cluster2.get().modify()->addTrack(tracks.back().get()); } cluster1.get().modify()->addTrack(tracks.back().get()); } tracks.emplace_back(session, "MyTrack" + std::to_string(tracks.size())); { auto transaction{ session.createWriteTransaction() }; tracks.back().get().modify()->setRelease(release3.get()); cluster2.get().modify()->addTrack(tracks.back().get()); } { auto transaction{ session.createReadTransaction() }; { auto releases{ release1->getSimilarReleases() }; ASSERT_EQ(releases.size(), 1); EXPECT_EQ(releases.front()->getId(), release2.getId()); } { auto releases{ release2->getSimilarReleases() }; ASSERT_EQ(releases.size(), 2); EXPECT_EQ(releases[0]->getId(), release1.getId()); EXPECT_EQ(releases[1]->getId(), release3.getId()); } } }