852 lines
32 KiB
C++
852 lines
32 KiB
C++
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Common.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <list>
|
|
|
|
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<ClusterId> 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<ScopedTrack> 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<ScopedTrack> tracks;
|
|
std::list<ScopedCluster> 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<ClusterId> 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<ScopedTrack> 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<ScopedTrack> 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<ScopedTrack> 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<TrackListEntry>(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<ScopedTrack> 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<TrackListEntry>(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<ScopedTrack> 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<ScopedTrack> 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());
|
|
}
|
|
}
|
|
}
|