Refactored namespaces

This commit is contained in:
emeric
2024-03-12 08:32:08 +01:00
parent 487960b413
commit 4b7c4295ec
501 changed files with 12605 additions and 12631 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ target_link_libraries(lmsdatabase PRIVATE
)
target_link_libraries(lmsdatabase PUBLIC
lmsutils
lmscore
std::filesystem
Wt::Dbo
)
+16 -16
View File
@@ -25,13 +25,13 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "database/User.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
#include "SqlQuery.hpp"
#include "Utils.hpp"
#include "EnumSetTraits.hpp"
#include "IdTypeTraits.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -67,16 +67,16 @@ namespace Database
for (std::string_view keyword : params.keywords)
{
clauses.push_back("a.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
query.bind("%" + utils::escapeLikeKeyword(keyword) + "%");
}
for (std::string_view keyword : params.keywords)
{
sortClauses.push_back("a.sort_name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'");
query.bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
query.bind("%" + utils::escapeLikeKeyword(keyword) + "%");
}
query.where("(" + StringUtils::joinStrings(clauses, " AND ") + ") OR (" + StringUtils::joinStrings(sortClauses, " AND ") + ")");
query.where("(" + core::stringUtils::joinStrings(clauses, " AND ") + ") OR (" + core::stringUtils::joinStrings(sortClauses, " AND ") + ")");
}
if (params.starringUser.isValid())
@@ -165,16 +165,16 @@ namespace Database
}
}
Artist::Artist(const std::string& name, const std::optional<UUID>& MBID)
Artist::Artist(const std::string& name, const std::optional<core::UUID>& MBID)
: _name{ std::string(name, 0 , _maxNameLength) },
_sortName{ _name },
_MBID{ MBID ? MBID->getAsString() : "" }
{
}
Artist::pointer Artist::create(Session& session, const std::string& name, const std::optional<UUID>& MBID)
Artist::pointer Artist::create(Session& session, const std::string& name, const std::optional<core::UUID>& MBID)
{
return session.getDboSession().add(std::unique_ptr<Artist> {new Artist{ name, MBID }});
return session.getDboSession().add(std::unique_ptr<Artist>{ new Artist{ name, MBID } });
}
std::size_t Artist::getCount(Session& session)
@@ -195,7 +195,7 @@ namespace Database
return std::vector<Artist::pointer>(res.begin(), res.end());
}
Artist::pointer Artist::find(Session& session, const UUID& mbid)
Artist::pointer Artist::find(Session& session, const core::UUID& mbid)
{
session.checkReadTransaction();
return session.getDboSession().find<Artist>().where("mbid = ?").bind(std::string{ mbid.getAsString() }).resultValue();
@@ -218,7 +218,7 @@ namespace Database
{
session.checkReadTransaction();
auto query{ session.getDboSession().query<ArtistId>("SELECT DISTINCT a.id FROM artist a WHERE NOT EXISTS(SELECT 1 FROM track t INNER JOIN track_artist_link t_a_l ON t_a_l.artist_id = a.id WHERE t.id = t_a_l.track_id)") };
return Utils::execQuery<ArtistId>(query, range);
return utils::execQuery<ArtistId>(query, range);
}
RangeResults<ArtistId> Artist::findIds(Session& session, const FindParameters& params)
@@ -226,7 +226,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<ArtistId>(session, params) };
return Utils::execQuery<ArtistId>(query, params.range);
return utils::execQuery<ArtistId>(query, params.range);
}
RangeResults<Artist::pointer> Artist::find(Session& session, const FindParameters& params)
@@ -234,7 +234,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Artist>>(session, params) };
return Utils::execQuery<Artist::pointer>(query, params.range);
return utils::execQuery<Artist::pointer>(query, params.range);
}
void Artist::find(Session& session, const FindParameters& params, std::function<void(const pointer&)> func)
@@ -242,10 +242,10 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Artist>>(session, params) };
Utils::execQuery(query, params.range, func);
utils::execQuery(query, params.range, func);
}
RangeResults<ArtistId> Artist::findSimilarArtistIds(EnumSet<TrackArtistLinkType> artistLinkTypes, std::optional<Range> range) const
RangeResults<ArtistId> Artist::findSimilarArtistIds(core::EnumSet<TrackArtistLinkType> artistLinkTypes, std::optional<Range> range) const
{
assert(session());
@@ -289,7 +289,7 @@ namespace Database
for (TrackArtistLinkType type : artistLinkTypes)
query.bind(type);
return Utils::execQuery<ArtistId>(query, range);
return utils::execQuery<ArtistId>(query, range);
}
std::vector<std::vector<Cluster::pointer>> Artist::getClusterGroups(std::vector<ClusterTypeId> clusterTypeIds, std::size_t size) const
@@ -338,4 +338,4 @@ namespace Database
_sortName = std::string(sortName, 0, _maxNameLength);
}
} // namespace Database
} // namespace lms::db
+1 -1
View File
@@ -25,7 +25,7 @@
#include "StringViewTraits.hpp"
#include "IdTypeTraits.hpp"
namespace Database
namespace lms::db
{
AuthToken::AuthToken(std::string_view value, const Wt::WDateTime& expiry, ObjectPtr<User> user)
+9 -9
View File
@@ -29,7 +29,7 @@
#include "SqlQuery.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -101,7 +101,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<ClusterId>(session, params) };
return Utils::execQuery<ClusterId>(query, params.range);
return utils::execQuery<ClusterId>(query, params.range);
}
RangeResults<Cluster::pointer> Cluster::find(Session& session, const FindParameters& params)
@@ -109,7 +109,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Cluster>>(session, params) };
return Utils::execQuery<Cluster::pointer>(query, params.range);
return utils::execQuery<Cluster::pointer>(query, params.range);
}
RangeResults<ClusterId> Cluster::findOrphanIds(Session& session, std::optional<Range> range)
@@ -117,7 +117,7 @@ namespace Database
session.checkReadTransaction();
auto query{ session.getDboSession().query<ClusterId>("SELECT DISTINCT c.id FROM cluster c WHERE NOT EXISTS(SELECT 1 FROM track_cluster t_c WHERE t_c.cluster_id = c.id)") };
return Utils::execQuery<ClusterId>(query, range);
return utils::execQuery<ClusterId>(query, range);
}
Cluster::pointer Cluster::find(Session& session, ClusterId id)
@@ -155,7 +155,7 @@ namespace Database
auto query{ session()->query<TrackId>("SELECT t.id FROM track t INNER JOIN cluster c ON c.id = t_c.cluster_id INNER JOIN track_cluster t_c ON t_c.track_id = t.id")
.where("c.id = ?").bind(getId()) };
return Utils::execQuery<TrackId>(query, range);
return utils::execQuery<TrackId>(query, range);
}
ClusterType::ClusterType(std::string_view name)
@@ -185,7 +185,7 @@ namespace Database
" LEFT OUTER JOIN cluster c ON c_t.id = c.cluster_type_id")
.where("c.id IS NULL") };
return Utils::execQuery<ClusterTypeId>(query, range);
return utils::execQuery<ClusterTypeId>(query, range);
}
RangeResults<ClusterTypeId> ClusterType::findUsed(Session& session, std::optional<Range> range)
@@ -196,7 +196,7 @@ namespace Database
"SELECT DISTINCT c_t.id from cluster_type c_t")
.join("cluster c ON c_t.id = c.cluster_type_id") };
return Utils::execQuery<ClusterTypeId>(query, range);
return utils::execQuery<ClusterTypeId>(query, range);
}
ClusterType::pointer ClusterType::find(Session& session, std::string_view name)
@@ -219,7 +219,7 @@ namespace Database
auto query{ session.getDboSession().query<ClusterTypeId>("SELECT id from cluster_type") };
return Utils::execQuery<ClusterTypeId>(query, range);
return utils::execQuery<ClusterTypeId>(query, range);
}
Cluster::pointer ClusterType::getCluster(const std::string& name) const
@@ -244,4 +244,4 @@ namespace Database
return std::vector<Cluster::pointer>(res.begin(), res.end());
}
} // namespace Database
} // namespace lms::db
+6 -6
View File
@@ -24,11 +24,11 @@
#include "database/Session.hpp"
#include "database/User.hpp"
#include "utils/IConfig.hpp"
#include "utils/Service.hpp"
#include "utils/ILogger.hpp"
#include "core/IConfig.hpp"
#include "core/Service.hpp"
#include "core/ILogger.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -89,7 +89,7 @@ namespace Database
LMS_LOG(DB, INFO, "Creating connection pool on file " << dbPath.string());
auto connection{ std::make_unique<Connection>(dbPath.string()) };
if (IConfig * config{ Service<IConfig>::get() })// may not be here on testU
if (core::IConfig * config{ core::Service<core::IConfig>::get() })// may not be here on testU
connection->setProperty("show-queries", config->getBool("db-show-queries", false) ? "true" : "false");
auto connectionPool{ std::make_unique<Wt::Dbo::FixedSqlConnectionPool>(std::move(connection), connectionCount) };
@@ -138,4 +138,4 @@ namespace Database
return _connection.get();
}
} // namespace Database
} // namespace lms::db
+5 -5
View File
@@ -22,22 +22,22 @@
#include <type_traits>
#include <Wt/Dbo/StdSqlTraits.h>
#include "utils/EnumSet.hpp"
#include "core/EnumSet.hpp"
namespace Wt::Dbo
{
template<typename T>
struct sql_value_traits<EnumSet<T>, void> : public sql_value_traits<long long>
struct sql_value_traits<lms::core::EnumSet<T>, void> : public sql_value_traits<long long>
{
using ValueType = typename EnumSet<T>::ValueType;
using ValueType = typename lms::core::EnumSet<T>::ValueType;
static_assert(sizeof(long long) > sizeof(ValueType));
static void bind(EnumSet<T> v, SqlStatement* statement, int column, int size)
static void bind(lms::core::EnumSet<T> v, SqlStatement* statement, int column, int size)
{
sql_value_traits<long long>::bind(static_cast<long long>(v.getBitfield()), statement, column, size);
}
static bool read(EnumSet<T>& v, SqlStatement* statement, int column, int size)
static bool read(lms::core::EnumSet<T>& v, SqlStatement* statement, int column, int size)
{
long long val;
if (sql_value_traits<long long>::read(val, statement, column, size))
+2 -2
View File
@@ -27,9 +27,9 @@
namespace Wt::Dbo
{
template<typename T>
struct sql_value_traits<T, typename std::enable_if<std::is_base_of<Database::IdType, T>::value>::type>
struct sql_value_traits<T, typename std::enable_if<std::is_base_of<lms::db::IdType, T>::value>::type>
{
static_assert(!std::is_same_v<Database::IdType, T>, "Cannot use IdType, use derived types");
static_assert(!std::is_same_v<lms::db::IdType, T>, "Cannot use IdType, use derived types");
static const bool specialized = true;
static std::string type(SqlConnection* conn, int size)
+9 -9
View File
@@ -25,7 +25,7 @@
#include "SqlQuery.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -205,7 +205,7 @@ namespace Database
if (parameters.syncState)
query.where("sync_state = ?").bind(*parameters.syncState);
return Utils::execQuery<ListenId>(query, parameters.range);
return utils::execQuery<ListenId>(query, parameters.range);
}
Listen::pointer Listen::find(Session& session, UserId userId, TrackId trackId, ScrobblingBackend backend, const Wt::WDateTime& dateTime)
@@ -229,7 +229,7 @@ namespace Database
.orderBy("COUNT(a.id) DESC")
.groupBy("a.id") };
return Utils::execQuery<ArtistId>(query, params.range);
return utils::execQuery<ArtistId>(query, params.range);
}
RangeResults<ReleaseId> Listen::getTopReleases(Session& session, const StatsFindParameters& params)
@@ -239,7 +239,7 @@ namespace Database
.orderBy("COUNT(r.id) DESC")
.groupBy("r.id") };
return Utils::execQuery<ReleaseId>(query, params.range);
return utils::execQuery<ReleaseId>(query, params.range);
}
RangeResults<TrackId> Listen::getTopTracks(Session& session, const StatsFindParameters& params)
@@ -249,7 +249,7 @@ namespace Database
.orderBy("COUNT(t.id) DESC")
.groupBy("t.id") };
return Utils::execQuery<TrackId>(query, params.range);
return utils::execQuery<TrackId>(query, params.range);
}
RangeResults<ArtistId> Listen::getRecentArtists(Session& session, const ArtistStatsFindParameters& params)
@@ -259,7 +259,7 @@ namespace Database
.groupBy("a.id").having("l.date_time = MAX(l.date_time)")
.orderBy("l.date_time DESC") };
return Utils::execQuery<ArtistId>(query, params.range);
return utils::execQuery<ArtistId>(query, params.range);
}
RangeResults<ReleaseId> Listen::getRecentReleases(Session& session, const StatsFindParameters& params)
@@ -269,7 +269,7 @@ namespace Database
.groupBy("r.id").having("l.date_time = MAX(l.date_time)")
.orderBy("l.date_time DESC") };
return Utils::execQuery<ReleaseId>(query, params.range);
return utils::execQuery<ReleaseId>(query, params.range);
}
RangeResults<TrackId> Listen::getRecentTracks(Session& session, const StatsFindParameters& params)
@@ -279,7 +279,7 @@ namespace Database
.groupBy("t.id").having("l.date_time = MAX(l.date_time)")
.orderBy("l.date_time DESC") };
return Utils::execQuery<TrackId>(query, params.range);
return utils::execQuery<TrackId>(query, params.range);
}
std::size_t Listen::getCount(Session& session, UserId userId, TrackId trackId)
@@ -339,4 +339,4 @@ namespace Database
.limit(1)
.resultValue();
}
} // namespace Database
} // namespace lms::db
+2 -2
View File
@@ -26,7 +26,7 @@
#include "PathTraits.hpp"
#include "StringViewTraits.hpp"
namespace Database
namespace lms::db
{
MediaLibrary::MediaLibrary(const std::filesystem::path& p, std::string_view name)
: _path{ p },
@@ -75,4 +75,4 @@ namespace Database
for (const auto& result : results)
func(result);
}
} // namespace Database
} // namespace lms::db
+7 -7
View File
@@ -25,10 +25,10 @@
#include "database/ScanSettings.hpp"
#include "database/Session.hpp"
#include "database/User.hpp"
#include "utils/Exception.hpp"
#include "utils/ILogger.hpp"
#include "core/Exception.hpp"
#include "core/ILogger.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -58,7 +58,7 @@ namespace Database
}
}
namespace Database::Migration
namespace lms::db::Migration
{
class ScopedNoForeignKeys
{
@@ -473,14 +473,14 @@ SELECT
catch (std::exception& e)
{
LMS_LOG(DB, ERROR, "Cannot get database version info: " << e.what());
throw LmsException{ outdatedMsg };
throw core::LmsException{ outdatedMsg };
}
if (version > LMS_DATABASE_VERSION)
throw LmsException{ "Server binary outdated, please upgrade it to handle this database" };
throw core::LmsException{ "Server binary outdated, please upgrade it to handle this database" };
if (version < migrationFunctions.begin()->first)
throw LmsException{ outdatedMsg };
throw core::LmsException{ outdatedMsg };
while (version < LMS_DATABASE_VERSION)
{
+1 -1
View File
@@ -21,7 +21,7 @@
#include <Wt/Dbo/Dbo.h>
namespace Database
namespace lms::db
{
class Session;
+12 -12
View File
@@ -26,14 +26,14 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "database/User.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
#include "SqlQuery.hpp"
#include "EnumSetTraits.hpp"
#include "IdTypeTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -76,7 +76,7 @@ namespace Database
}
for (std::string_view keyword : params.keywords)
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
query.where("r.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
if (params.starringUser.isValid())
{
@@ -227,13 +227,13 @@ namespace Database
.resultValue();
}
Release::Release(const std::string& name, const std::optional<UUID>& MBID)
Release::Release(const std::string& name, const std::optional<core::UUID>& MBID)
: _name{ std::string(name, 0 , _maxNameLength) },
_MBID{ MBID ? MBID->getAsString() : "" }
{
}
Release::pointer Release::create(Session& session, const std::string& name, const std::optional<UUID>& MBID)
Release::pointer Release::create(Session& session, const std::string& name, const std::optional<core::UUID>& MBID)
{
return session.getDboSession().add(std::unique_ptr<Release> {new Release{ name, MBID }});
}
@@ -246,13 +246,13 @@ namespace Database
.query<Wt::Dbo::ptr<Release>>("SELECT DISTINCT r from release r")
.join("track t ON t.release_id = r.id")
.where("r.name = ?").bind(std::string(name, 0, _maxNameLength))
.where("t.file_path LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind(Utils::escapeLikeKeyword(releaseDirectory.string()) + "%")
.where("t.file_path LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind(utils::escapeLikeKeyword(releaseDirectory.string()) + "%")
.resultList() };
return std::vector<Release::pointer>(res.begin(), res.end());
}
Release::pointer Release::find(Session& session, const UUID& mbid)
Release::pointer Release::find(Session& session, const core::UUID& mbid)
{
session.checkReadTransaction();
@@ -290,7 +290,7 @@ namespace Database
session.checkReadTransaction();
auto query{ session.getDboSession().query<ReleaseId>("select r.id from release r LEFT OUTER JOIN Track t ON r.id = t.release_id WHERE t.id IS NULL") };
return Utils::execQuery<ReleaseId>(query, range);
return utils::execQuery<ReleaseId>(query, range);
}
RangeResults<Release::pointer> Release::find(Session& session, const FindParameters& params)
@@ -298,7 +298,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Release>>(session, "DISTINCT r", params) };
return Utils::execQuery<pointer>(query, params.range);
return utils::execQuery<pointer>(query, params.range);
}
void Release::find(Session& session, const FindParameters& params, std::function<void(const pointer&)> func)
@@ -306,7 +306,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Release>>(session, "DISTINCT r", params) };
Utils::execQuery<pointer>(query, params.range, func);
utils::execQuery<pointer>(query, params.range, func);
}
RangeResults<ReleaseId> Release::findIds(Session& session, const FindParameters& params)
@@ -314,7 +314,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<ReleaseId>(session, "DISTINCT r.id", params) };
return Utils::execQuery<ReleaseId>(query, params.range);
return utils::execQuery<ReleaseId>(query, params.range);
}
std::size_t Release::getCount(Session& session, const FindParameters& params)
@@ -602,4 +602,4 @@ namespace Database
return res;
}
} // namespace Database
} // namespace lms::db
+10 -10
View File
@@ -23,9 +23,9 @@
#include "database/MediaLibrary.hpp"
#include "database/Session.hpp"
#include "utils/String.hpp"
#include "core/String.hpp"
namespace Database
namespace lms::db
{
void ScanSettings::init(Session& session)
{
@@ -46,7 +46,7 @@ namespace Database
std::vector<std::filesystem::path> ScanSettings::getAudioFileExtensions() const
{
const auto extensions{ StringUtils::splitString(_audioFileExtensions, ' ') };
const auto extensions{ core::stringUtils::splitString(_audioFileExtensions, ' ') };
std::vector<std::filesystem::path> res(std::cbegin(extensions), std::cend(extensions));
std::sort(std::begin(res), std::end(res));
@@ -57,22 +57,22 @@ namespace Database
std::vector<std::string_view> ScanSettings::getExtraTagsToScan() const
{
return StringUtils::splitString(_extraTagsToScan, ';');
return core::stringUtils::splitString(_extraTagsToScan, ';');
}
std::vector<std::string> ScanSettings::getArtistTagDelimiters() const
{
return StringUtils::splitEscapedStrings(_artistTagDelimiters, ';', '\\');
return core::stringUtils::splitEscapedStrings(_artistTagDelimiters, ';', '\\');
}
std::vector<std::string> ScanSettings::getDefaultTagDelimiters() const
{
return StringUtils::splitEscapedStrings(_defaultTagDelimiters, ';', '\\');
return core::stringUtils::splitEscapedStrings(_defaultTagDelimiters, ';', '\\');
}
void ScanSettings::setExtraTagsToScan(const std::vector<std::string_view>& extraTags)
{
std::string newTagsToScan{ StringUtils::joinStrings(extraTags, ";") };
std::string newTagsToScan{ core::stringUtils::joinStrings(extraTags, ";") };
if (newTagsToScan != _extraTagsToScan)
incScanVersion();
@@ -81,7 +81,7 @@ namespace Database
void ScanSettings::setArtistTagDelimiters(std::span<const std::string_view> delimiters)
{
std::string tagDelimiters{ StringUtils::escapeAndJoinStrings(delimiters, ';', '\\') };
std::string tagDelimiters{ core::stringUtils::escapeAndJoinStrings(delimiters, ';', '\\') };
if (tagDelimiters != _artistTagDelimiters)
{
_artistTagDelimiters.swap(tagDelimiters);
@@ -91,7 +91,7 @@ namespace Database
void ScanSettings::setDefaultTagDelimiters(std::span<const std::string_view> delimiters)
{
std::string tagDelimiters{ StringUtils::escapeAndJoinStrings(delimiters, ';', '\\') };
std::string tagDelimiters{ core::stringUtils::escapeAndJoinStrings(delimiters, ';', '\\') };
if (tagDelimiters != _defaultTagDelimiters)
{
_defaultTagDelimiters.swap(tagDelimiters);
@@ -103,4 +103,4 @@ namespace Database
{
_scanVersion += 1;
}
} // namespace Database
} // namespace lms::db
+6 -7
View File
@@ -21,9 +21,9 @@
#include <cassert>
#include "utils/Exception.hpp"
#include "utils/ILogger.hpp"
#include "utils/ITraceLogger.hpp"
#include "core/Exception.hpp"
#include "core/ILogger.hpp"
#include "core/ITraceLogger.hpp"
#include "database/Artist.hpp"
#include "database/AuthToken.hpp"
@@ -47,10 +47,9 @@
#include "PathTraits.hpp"
#include "Migration.hpp"
namespace Database
namespace lms::db
{
WriteTransaction::WriteTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
WriteTransaction::WriteTransaction(core::RecursiveSharedMutex& mutex, Wt::Dbo::Session& session)
: _lock{ mutex },
_transaction{ session }
{
@@ -210,4 +209,4 @@ namespace Database
LMS_LOG(DB, INFO, "Database optimizing complete");
}
} // namespace Database
} // namespace lms::db
+127 -138
View File
@@ -23,177 +23,166 @@
#include <cassert>
#include <sstream>
WhereClause&
WhereClause::And(const WhereClause& otherClause)
namespace lms::db
{
if (!otherClause._clause.empty()) {
if (!_clause.empty())
_clause += " AND ";
_clause += "(" + otherClause._clause + ")";
WhereClause& WhereClause::And(const WhereClause& otherClause)
{
if (!otherClause._clause.empty()) {
if (!_clause.empty())
_clause += " AND ";
_clause += "(" + otherClause._clause + ")";
// Add associated bind args
for (const std::string& otherBindArg : otherClause._bindArgs)
{
_bindArgs.push_back(otherBindArg);
}
}
return *this;
}
// Add associated bind args
for (const std::string& otherBindArg : otherClause._bindArgs)
{
_bindArgs.push_back(otherBindArg);
}
}
return *this;
}
WhereClause&
WhereClause::Or(const WhereClause& otherClause)
{
if (!otherClause._clause.empty()) {
if (!_clause.empty())
_clause += " OR ";
_clause += "(" + otherClause._clause + ")";
WhereClause& WhereClause::Or(const WhereClause& otherClause)
{
if (!otherClause._clause.empty()) {
if (!_clause.empty())
_clause += " OR ";
_clause += "(" + otherClause._clause + ")";
// Add associated bind args
for (const std::string& otherBindArg : otherClause._bindArgs)
{
_bindArgs.push_back(otherBindArg);
}
}
return *this;
}
// Add associated bind args
for (const std::string& otherBindArg : otherClause._bindArgs)
{
_bindArgs.push_back(otherBindArg);
}
}
return *this;
}
std::string
WhereClause::get() const
{
if (!_clause.empty())
return "WHERE " + _clause;
else
return "";
}
std::string WhereClause::get() const
{
if (!_clause.empty())
return "WHERE " + _clause;
else
return "";
}
WhereClause&
WhereClause::bind(std::string_view bindArg)
{
assert(_bindArgs.size() < static_cast<std::size_t>(std::count(_clause.begin(), _clause.end(), '?')));
WhereClause& WhereClause::bind(std::string_view bindArg)
{
assert(_bindArgs.size() < static_cast<std::size_t>(std::count(_clause.begin(), _clause.end(), '?')));
_bindArgs.push_back(std::string{ bindArg });
_bindArgs.push_back(std::string{ bindArg });
return *this;
}
return *this;
}
InnerJoinClause::InnerJoinClause(const std::string& clause)
:_clause(clause)
{
}
InnerJoinClause::InnerJoinClause(const std::string& clause)
:_clause(clause)
{
}
InnerJoinClause&
InnerJoinClause::And(const InnerJoinClause& clause)
{
if (!_clause.empty())
_clause += " ";
InnerJoinClause& InnerJoinClause::And(const InnerJoinClause& clause)
{
if (!_clause.empty())
_clause += " ";
_clause += "INNER JOIN " + clause._clause;
_clause += "INNER JOIN " + clause._clause;
return *this;
}
return *this;
}
SelectStatement::SelectStatement(const std::string& statement)
{
And(statement);
}
SelectStatement::SelectStatement(const std::string& statement)
{
And(statement);
}
SelectStatement&
SelectStatement::And(const std::string& statement)
{
_statement.push_back(statement);
SelectStatement& SelectStatement::And(const std::string& statement)
{
_statement.push_back(statement);
std::sort(_statement.begin(), _statement.end());
_statement.erase(std::unique(_statement.begin(), _statement.end()), _statement.end());
std::sort(_statement.begin(), _statement.end());
_statement.erase(std::unique(_statement.begin(), _statement.end()), _statement.end());
return *this;
}
return *this;
}
std::string
SelectStatement::get() const
{
std::string res = "SELECT ";
std::string SelectStatement::get() const
{
std::string res = "SELECT ";
for (auto it = _statement.begin(); it != _statement.end(); ++it)
{
if (it != _statement.begin())
res += ",";
res += *it;
}
for (auto it = _statement.begin(); it != _statement.end(); ++it)
{
if (it != _statement.begin())
res += ",";
res += *it;
}
return res;
}
return res;
}
GroupByStatement& GroupByStatement::And(const GroupByStatement& statement)
{
if (_statement.empty() && !statement._statement.empty())
_statement = "GROUP BY ";
else if (!_statement.empty() && !statement._statement.empty())
_statement += ",";
_statement += statement._statement;
return *this;
}
GroupByStatement&
GroupByStatement::And(const GroupByStatement& statement)
{
if( _statement.empty() && !statement._statement.empty())
_statement = "GROUP BY ";
else if (!_statement.empty() && !statement._statement.empty())
_statement += ",";
FromClause::FromClause(const std::string& clause)
{
_clause.push_back(clause);
}
_statement += statement._statement;
return *this;
}
FromClause& FromClause::And(const FromClause& clause)
{
for (const std::string& fromClause : clause._clause)
{
_clause.push_back(fromClause);
}
FromClause::FromClause(const std::string& clause)
{
_clause.push_back(clause);
}
std::sort(_clause.begin(), _clause.end());
_clause.erase(std::unique(_clause.begin(), _clause.end()), _clause.end());
FromClause&
FromClause::And(const FromClause& clause)
{
for (const std::string& fromClause : clause._clause)
{
_clause.push_back(fromClause);
}
return *this;
}
std::sort(_clause.begin(), _clause.end());
_clause.erase(std::unique(_clause.begin(), _clause.end()), _clause.end());
std::string FromClause::get() const
{
std::ostringstream oss;
return *this;
}
if (!_clause.empty())
{
oss << "FROM ";
for (auto it = _clause.begin(); it != _clause.end(); ++it) {
if (it != _clause.begin())
oss << ",";
std::string
FromClause::get() const
{
std::ostringstream oss;
oss << *it;
}
}
if (!_clause.empty())
{
oss << "FROM ";
for (auto it = _clause.begin(); it != _clause.end(); ++it) {
if (it != _clause.begin())
oss << ",";
return oss.str();
}
oss << *it;
}
}
std::string SqlQuery::get() const
{
std::ostringstream oss;
return oss.str();
}
oss << _selectStatement.get();
std::string
SqlQuery::get() const
{
std::ostringstream oss;
if (!_fromClause.get().empty())
oss << " " << _fromClause.get();
oss << _selectStatement.get();
if (!_innerJoinClause.get().empty())
oss << " " << _innerJoinClause.get();
if (!_fromClause.get().empty())
oss << " " << _fromClause.get();
if (!_whereClause.get().empty())
oss << " " << _whereClause.get();
if (!_innerJoinClause.get().empty())
oss << " " << _innerJoinClause.get();
if (!_whereClause.get().empty())
oss << " " << _whereClause.get();
if (!_groupByStatement.get().empty())
oss << " " << _groupByStatement.get();
return oss.str();
}
if (!_groupByStatement.get().empty())
oss << " " << _groupByStatement.get();
return oss.str();
}
}
+77 -76
View File
@@ -22,102 +22,103 @@
#include <vector>
#include <string>
class WhereClause
namespace lms::db
{
public:
WhereClause() {}
WhereClause(const std::string& clause) { _clause = clause; }
class WhereClause
{
public:
WhereClause() {}
WhereClause(const std::string& clause) { _clause = clause; }
WhereClause& And(const WhereClause& clause);
WhereClause& Or(const WhereClause& clause);
WhereClause& And(const WhereClause& clause);
WhereClause& Or(const WhereClause& clause);
// Arguments binding (for each '?' in where clause)
WhereClause& bind(std::string_view arg);
// Arguments binding (for each '?' in where clause)
WhereClause& bind(std::string_view arg);
std::string get() const;
const std::vector<std::string>& getBindArgs() const {return _bindArgs;}
std::string get() const;
const std::vector<std::string>& getBindArgs() const { return _bindArgs; }
private:
std::string _clause; // WHERE clause
std::vector<std::string> _bindArgs;
};
private:
std::string _clause; // WHERE clause
std::vector<std::string> _bindArgs;
};
class InnerJoinClause
{
public:
InnerJoinClause() {}
InnerJoinClause(const std::string& clause);
class InnerJoinClause
{
public:
InnerJoinClause() {}
InnerJoinClause(const std::string& clause);
InnerJoinClause& And(const InnerJoinClause& clause);
std::string get() const { return _clause;}
InnerJoinClause& And(const InnerJoinClause& clause);
std::string get() const { return _clause; }
private:
std::string _clause;
};
private:
std::string _clause;
};
class GroupByStatement
{
public:
GroupByStatement() {}
GroupByStatement(const std::string& statement) { _statement = statement; }
class GroupByStatement
{
public:
GroupByStatement() {}
GroupByStatement(const std::string& statement) { _statement = statement; }
GroupByStatement& And(const GroupByStatement& statement);
GroupByStatement& And(const GroupByStatement& statement);
std::string get() const {return _statement;}
std::string get() const { return _statement; }
private:
std::string _statement; // SELECT statement
};
private:
std::string _statement; // SELECT statement
};
class SelectStatement
{
public:
SelectStatement() {};
SelectStatement(const std::string& item);
class SelectStatement
{
public:
SelectStatement() {};
SelectStatement(const std::string& item);
SelectStatement& And(const std::string& item);
SelectStatement& And(const std::string& item);
std::string get() const;
std::string get() const;
private:
std::vector<std::string> _statement;
};
private:
std::vector<std::string> _statement;
};
class FromClause
{
public:
FromClause() {}
FromClause(const std::string& clause);
class FromClause
{
public:
FromClause() {}
FromClause(const std::string& clause);
FromClause& And(const FromClause& clause);
FromClause& And(const FromClause& clause);
std::string get() const;
std::string get() const;
private:
std::vector<std::string> _clause;
};
private:
std::vector<std::string> _clause;
};
class SqlQuery
{
public:
SelectStatement& select() { return _selectStatement;}
SelectStatement& select(const std::string& statement) { _selectStatement = SelectStatement(statement); return _selectStatement; }
FromClause& from() { return _fromClause; }
FromClause& from(const std::string& clause) { _whereClause = WhereClause(clause); return _fromClause; }
InnerJoinClause& innerJoin() { return _innerJoinClause; }
WhereClause& where() { return _whereClause; }
const WhereClause& where() const { return _whereClause; }
GroupByStatement& groupBy() { return _groupByStatement; }
const GroupByStatement& groupBy() const { return _groupByStatement; }
class SqlQuery
{
public:
SelectStatement& select() { return _selectStatement; }
SelectStatement& select(const std::string& statement) { _selectStatement = SelectStatement(statement); return _selectStatement; }
FromClause& from() { return _fromClause; }
FromClause& from(const std::string& clause) { _whereClause = WhereClause(clause); return _fromClause; }
InnerJoinClause& innerJoin() { return _innerJoinClause; }
WhereClause& where() { return _whereClause; }
const WhereClause& where() const { return _whereClause; }
GroupByStatement& groupBy() { return _groupByStatement; }
const GroupByStatement& groupBy() const { return _groupByStatement; }
std::string get() const;
private:
SelectStatement _selectStatement; // SELECT statement
InnerJoinClause _innerJoinClause; // INNER JOIN
FromClause _fromClause; // FROM tables
WhereClause _whereClause; // WHERE clause
GroupByStatement _groupByStatement; // GROUP BY statement
};
std::string get() const;
private:
SelectStatement _selectStatement; // SELECT statement
InnerJoinClause _innerJoinClause; // INNER JOIN
FromClause _fromClause; // FROM tables
WhereClause _whereClause; // WHERE clause
GroupByStatement _groupByStatement; // GROUP BY statement
};
}
+2 -2
View File
@@ -27,7 +27,7 @@
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
StarredArtist::StarredArtist(ObjectPtr<Artist> artist, ObjectPtr<User> user, FeedbackBackend backend)
: _backend{ backend }
@@ -76,6 +76,6 @@ namespace Database
void StarredArtist::setDateTime(const Wt::WDateTime& dateTime)
{
_dateTime = Utils::normalizeDateTime(dateTime);
_dateTime = utils::normalizeDateTime(dateTime);
}
}
+2 -2
View File
@@ -27,7 +27,7 @@
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
StarredRelease::StarredRelease(ObjectPtr<Release> release, ObjectPtr<User> user, FeedbackBackend backend)
: _backend{ backend }
@@ -76,6 +76,6 @@ namespace Database
void StarredRelease::setDateTime(const Wt::WDateTime& dateTime)
{
_dateTime = Utils::normalizeDateTime(dateTime);
_dateTime = utils::normalizeDateTime(dateTime);
}
}
+3 -3
View File
@@ -27,7 +27,7 @@
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
StarredTrack::StarredTrack(ObjectPtr<Track> track, ObjectPtr<User> user, FeedbackBackend backend)
: _backend{ backend }
@@ -96,11 +96,11 @@ namespace Database
if (params.user.isValid())
query.where("s_t.user_id = ?").bind(params.user);
return Utils::execQuery<StarredTrackId>(query, params.range);
return utils::execQuery<StarredTrackId>(query, params.range);
}
void StarredTrack::setDateTime(const Wt::WDateTime& dateTime)
{
_dateTime = Utils::normalizeDateTime(dateTime);
_dateTime = utils::normalizeDateTime(dateTime);
}
}
+15 -15
View File
@@ -29,14 +29,14 @@
#include "database/TrackFeatures.hpp"
#include "database/Session.hpp"
#include "database/User.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
#include "IdTypeTraits.hpp"
#include "SqlQuery.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -49,7 +49,7 @@ namespace Database
assert(params.keywords.empty() || params.name.empty());
for (std::string_view keyword : params.keywords)
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + Utils::escapeLikeKeyword(keyword) + "%");
query.where("t.name LIKE ? ESCAPE '" ESCAPE_CHAR_STR "'").bind("%" + utils::escapeLikeKeyword(keyword) + "%");
if (!params.name.empty())
query.where("t.name = ?").bind(params.name);
@@ -235,7 +235,7 @@ namespace Database
return session.getDboSession().query<int>("SELECT 1 from track").where("id = ?").bind(id).resultValue() == 1;
}
std::vector<Track::pointer> Track::findByMBID(Session& session, const UUID& mbid)
std::vector<Track::pointer> Track::findByMBID(Session& session, const core::UUID& mbid)
{
session.checkReadTransaction();
@@ -246,7 +246,7 @@ namespace Database
return std::vector<Track::pointer>(res.begin(), res.end());
}
std::vector<Track::pointer> Track::findByRecordingMBID(Session& session, const UUID& mbid)
std::vector<Track::pointer> Track::findByRecordingMBID(Session& session, const core::UUID& mbid)
{
session.checkReadTransaction();
@@ -265,7 +265,7 @@ namespace Database
// TODO Dbo traits on filesystem
auto query{ session.getDboSession().query<QueryResultType>("SELECT id, file_path FROM track") };
RangeResults<QueryResultType> queryResults{ Utils::execQuery<QueryResultType>(query, range) };
RangeResults<QueryResultType> queryResults{ utils::execQuery<QueryResultType>(query, range) };
RangeResults<PathResult> res;
res.range = queryResults.range;
@@ -288,7 +288,7 @@ namespace Database
auto query{ session.getDboSession().query<TrackId>("SELECT track.id FROM track WHERE mbid in (SELECT mbid FROM track WHERE mbid <> '' GROUP BY mbid HAVING COUNT (*) > 1)")
.orderBy("track.release_id,track.disc_number,track.track_number,track.mbid") };
return Utils::execQuery<TrackId>(query, range);
return utils::execQuery<TrackId>(query, range);
}
RangeResults<TrackId> Track::findIdsWithRecordingMBIDAndMissingFeatures(Session& session, std::optional<Range> range)
@@ -299,7 +299,7 @@ namespace Database
.where("LENGTH(t.recording_mbid) > 0")
.where("NOT EXISTS (SELECT * FROM track_features t_f WHERE t_f.track_id = t.id)") };
return Utils::execQuery<TrackId>(query, range);
return utils::execQuery<TrackId>(query, range);
}
std::vector<Cluster::pointer> Track::getClusters() const
@@ -324,7 +324,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<TrackId>(session, parameters) };
return Utils::execQuery<TrackId>(query, parameters.range);
return utils::execQuery<TrackId>(query, parameters.range);
}
RangeResults<Track::pointer> Track::find(Session& session, const FindParameters& parameters)
@@ -332,7 +332,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Track>>(session, parameters) };
return Utils::execQuery<Track::pointer>(query, parameters.range);
return utils::execQuery<Track::pointer>(query, parameters.range);
}
void Track::find(Session& session, const FindParameters& params, std::function<void(const Track::pointer&)> func)
@@ -340,7 +340,7 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery<Wt::Dbo::ptr<Track>>(session, params)};
Utils::execQuery(query, params.range, func);
utils::execQuery(query, params.range, func);
}
RangeResults<TrackId> Track::findSimilarTrackIds(Session& session, const std::vector<TrackId>& tracks, std::optional<Range> range)
@@ -370,7 +370,7 @@ namespace Database
for (TrackId trackId : tracks)
query.bind(trackId);
return Utils::execQuery<TrackId>(query, range);
return utils::execQuery<TrackId>(query, range);
}
void Track::clearArtistLinks()
@@ -400,7 +400,7 @@ namespace Database
return _copyrightURL != "" ? std::make_optional<std::string>(_copyrightURL) : std::nullopt;
}
std::vector<Artist::pointer> Track::getArtists(EnumSet<TrackArtistLinkType> linkTypes) const
std::vector<Artist::pointer> Track::getArtists(core::EnumSet<TrackArtistLinkType> linkTypes) const
{
assert(session());
@@ -435,7 +435,7 @@ namespace Database
return std::vector<Artist::pointer>(std::begin(res), std::end(res));
}
std::vector<ArtistId> Track::getArtistIds(EnumSet<TrackArtistLinkType> linkTypes) const
std::vector<ArtistId> Track::getArtistIds(core::EnumSet<TrackArtistLinkType> linkTypes) const
{
assert(self());
assert(session());
@@ -544,4 +544,4 @@ namespace Database
}
}
} // namespace Database
} // namespace lms::db
+6 -6
View File
@@ -26,7 +26,7 @@
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
namespace
{
@@ -84,19 +84,19 @@ namespace Database
session.checkReadTransaction();
auto query{ createQuery(session, params) };
return Utils::execQuery<TrackArtistLinkId>(query, params.range);
return utils::execQuery<TrackArtistLinkId>(query, params.range);
}
EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session)
core::EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session)
{
session.checkReadTransaction();
auto res{ session.getDboSession().query<TrackArtistLinkType>("SELECT DISTINCT type from track_artist_link").resultList() };
return EnumSet<TrackArtistLinkType>(std::begin(res), std::end(res));
return core::EnumSet<TrackArtistLinkType>(std::begin(res), std::end(res));
}
EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session, ArtistId artistId)
core::EnumSet<TrackArtistLinkType> TrackArtistLink::findUsedTypes(Session& session, ArtistId artistId)
{
session.checkReadTransaction();
@@ -105,7 +105,7 @@ namespace Database
.where("artist_id = ?").bind(artistId)
.resultList() };
return EnumSet<TrackArtistLinkType>(std::begin(res), std::end(res));
return core::EnumSet<TrackArtistLinkType>(std::begin(res), std::end(res));
}
}
+4 -4
View File
@@ -25,8 +25,8 @@
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
namespace Database {
namespace lms::db
{
TrackBookmark::TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track)
: _user{ getDboPtr(user) },
_track{ getDboPtr(track) }
@@ -52,7 +52,7 @@ namespace Database {
auto query{ session.getDboSession().query<TrackBookmarkId>("SELECT id from track_bookmark")
.where("user_id = ?").bind(userId) };
return Utils::execQuery<TrackBookmarkId>(query, range);
return utils::execQuery<TrackBookmarkId>(query, range);
}
TrackBookmark::pointer TrackBookmark::find(Session& session, UserId userId, TrackId trackId)
@@ -74,5 +74,5 @@ namespace Database {
.resultValue();
}
} // namespace Database
} // namespace lms::db
+4 -4
View File
@@ -24,11 +24,11 @@
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
namespace Database {
namespace lms::db {
TrackFeatures::TrackFeatures(ObjectPtr<Track> track, const std::string& jsonEncodedFeatures)
: _data{ jsonEncodedFeatures },
@@ -72,7 +72,7 @@ namespace Database {
auto query{ session.getDboSession().query<TrackFeaturesId>("SELECT id from track_features") };
return Utils::execQuery<TrackFeaturesId>(query, range);
return utils::execQuery<TrackFeaturesId>(query, range);
}
FeatureValues TrackFeatures::getFeatureValues(const FeatureName& featureNode) const
@@ -118,4 +118,4 @@ namespace Database {
return res;
}
} // namespace Database
} // namespace lms::db
+11 -12
View File
@@ -20,7 +20,7 @@
#include <cassert>
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
#include "database/Artist.hpp"
#include "database/Cluster.hpp"
@@ -33,14 +33,14 @@
#include "IdTypeTraits.hpp"
#include "Utils.hpp"
namespace Database
namespace lms::db
{
TrackList::TrackList(std::string_view name, TrackListType type, bool isPublic, ObjectPtr<User> user)
: _name{ name }
, _type{ type }
, _isPublic{ isPublic }
, _creationDateTime{ Utils::normalizeDateTime(Wt::WDateTime::currentDateTime()) }
, _lastModifiedDateTime{ Utils::normalizeDateTime(Wt::WDateTime::currentDateTime()) }
, _creationDateTime{ utils::normalizeDateTime(Wt::WDateTime::currentDateTime()) }
, _lastModifiedDateTime{ utils::normalizeDateTime(Wt::WDateTime::currentDateTime()) }
, _user{ getDboPtr(user) }
{
assert(user);
@@ -117,7 +117,7 @@ namespace Database
break;
}
return Utils::execQuery<TrackListId>(query, params.range);
return utils::execQuery<TrackListId>(query, params.range);
}
TrackList::pointer TrackList::find(Session& session, TrackListId id)
@@ -170,7 +170,7 @@ namespace Database
return session()->find<TrackListEntry>()
.where("tracklist_id = ?").bind(getId())
.where("track_id = ?").bind(track->getId())
.where("date_time = ?").bind(Utils::normalizeDateTime(dateTime))
.where("date_time = ?").bind(utils::normalizeDateTime(dateTime))
.resultValue();
}
@@ -294,11 +294,11 @@ namespace Database
void TrackList::setLastModifiedDateTime(const Wt::WDateTime& dateTime)
{
_lastModifiedDateTime = Utils::normalizeDateTime(dateTime);
_lastModifiedDateTime = utils::normalizeDateTime(dateTime);
}
TrackListEntry::TrackListEntry(ObjectPtr<Track> track, ObjectPtr<TrackList> tracklist, const Wt::WDateTime& dateTime)
: _dateTime{ Utils::normalizeDateTime(dateTime) }
: _dateTime{ utils::normalizeDateTime(dateTime) }
, _track{ getDboPtr(track) }
, _tracklist{ getDboPtr(tracklist) }
{
@@ -313,12 +313,12 @@ namespace Database
void TrackListEntry::onPostCreated()
{
_tracklist.modify()->setLastModifiedDateTime(Utils::normalizeDateTime(Wt::WDateTime::currentDateTime()));
_tracklist.modify()->setLastModifiedDateTime(utils::normalizeDateTime(Wt::WDateTime::currentDateTime()));
}
void TrackListEntry::onPreRemove()
{
_tracklist.modify()->setLastModifiedDateTime(Utils::normalizeDateTime(Wt::WDateTime::currentDateTime()));
_tracklist.modify()->setLastModifiedDateTime(utils::normalizeDateTime(Wt::WDateTime::currentDateTime()));
}
TrackListEntry::pointer TrackListEntry::getById(Session& session, TrackListEntryId id)
@@ -327,5 +327,4 @@ namespace Database
return session.getDboSession().find<TrackListEntry>().where("id = ?").bind(id).resultValue();
}
} // namespace Database
} // namespace lms::db
@@ -29,7 +29,7 @@
#define LMS_CHECK_TRANSACTION_ACCESSES 0
#endif
namespace Database
namespace lms::db
{
#if LMS_CHECK_TRANSACTION_ACCESSES
namespace
+1 -1
View File
@@ -21,7 +21,7 @@
#include <set>
namespace Database
namespace lms::db
{
static const std::set<Bitrate> allowedAudioBitrates
{
+4 -4
View File
@@ -23,12 +23,12 @@
#include "database/Release.hpp"
#include "database/Session.hpp"
#include "database/Track.hpp"
#include "utils/ILogger.hpp"
#include "core/ILogger.hpp"
#include "IdTypeTraits.hpp"
#include "StringViewTraits.hpp"
#include "Utils.hpp"
namespace Database {
namespace lms::db {
User::User(std::string_view loginName)
: _loginName{ loginName }
@@ -58,7 +58,7 @@ namespace Database {
if (params.feedbackBackend)
query.where("feedback_backend = ?").bind(*params.feedbackBackend);
return Utils::execQuery<UserId>(query, params.range);
return utils::execQuery<UserId>(query, params.range);
}
User::pointer User::findDemoUser(Session& session)
@@ -91,4 +91,4 @@ namespace Database {
_authTokens.clear();
}
} // namespace Database
} // namespace lms::db
+12 -14
View File
@@ -19,21 +19,19 @@
#include "Utils.hpp"
#include "utils/String.hpp"
#include "core/String.hpp"
namespace Database::Utils
namespace lms::db::utils
{
std::string
escapeLikeKeyword(std::string_view keyword)
{
return StringUtils::escapeString(keyword, "%_", escapeChar);
}
std::string escapeLikeKeyword(std::string_view keyword)
{
return core::stringUtils::escapeString(keyword, "%_", escapeChar);
}
Wt::WDateTime
normalizeDateTime(const Wt::WDateTime& dateTime)
{
// force second resolution
return Wt::WDateTime::fromTime_t(dateTime.toTime_t());
}
} // namespace Database::Utils
Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime)
{
// force second resolution
return Wt::WDateTime::fromTime_t(dateTime.toTime_t());
}
} // namespace lms::db::Utils
+3 -3
View File
@@ -27,9 +27,9 @@
#include <Wt/WDateTime.h>
#include "database/Types.hpp"
#include "utils/ITraceLogger.hpp"
#include "core/ITraceLogger.hpp"
namespace Database::Utils
namespace lms::db::utils
{
#define ESCAPE_CHAR_STR "\\"
static inline constexpr char escapeChar{ '\\' };
@@ -86,5 +86,5 @@ namespace Database::Utils
}
Wt::WDateTime normalizeDateTime(const Wt::WDateTime& dateTime);
} // namespace Database::Utils
}
+10 -10
View File
@@ -27,6 +27,8 @@
#include <Wt/WDateTime.h>
#include <Wt/Dbo/Dbo.h>
#include "core/EnumSet.hpp"
#include "core/UUID.hpp"
#include "database/ArtistId.hpp"
#include "database/ClusterId.hpp"
#include "database/MediaLibraryId.hpp"
@@ -35,10 +37,8 @@
#include "database/Types.hpp"
#include "database/UserId.hpp"
#include "database/TrackId.hpp"
#include "utils/EnumSet.hpp"
#include "utils/UUID.hpp"
namespace Database
namespace lms::db
{
class Cluster;
@@ -83,7 +83,7 @@ namespace Database
// Accessors
static std::size_t getCount(Session& session);
static pointer find(Session& session, const UUID& MBID);
static pointer find(Session& session, const core::UUID& MBID);
static pointer find(Session& session, ArtistId id);
static std::vector<pointer> find(Session& session, std::string_view name); // exact match on name field
static RangeResults<pointer> find(Session& session, const FindParameters& parameters);
@@ -95,10 +95,10 @@ namespace Database
// Accessors
const std::string& getName() const { return _name; }
const std::string& getSortName() const { return _sortName; }
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
std::optional<core::UUID> getMBID() const { return core::UUID::fromString(_MBID); }
// No artistLinkTypes means get them all
RangeResults<ArtistId> findSimilarArtistIds(EnumSet<TrackArtistLinkType> artistLinkTypes = {}, std::optional<Range> range = std::nullopt) const;
RangeResults<ArtistId> findSimilarArtistIds(core::EnumSet<TrackArtistLinkType> artistLinkTypes = {}, std::optional<Range> range = std::nullopt) const;
// Get the cluster of the tracks made by this artist
// Each clusters are grouped by cluster type, sorted by the number of occurence
@@ -106,7 +106,7 @@ namespace Database
std::vector<std::vector<ObjectPtr<Cluster>>> getClusterGroups(std::vector<ClusterTypeId> clusterTypeIds, std::size_t size) const;
void setName(std::string_view name) { _name = name; }
void setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
void setMBID(const std::optional<core::UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
void setSortName(const std::string& sortName);
template<class Action>
@@ -125,8 +125,8 @@ namespace Database
friend class Session;
// Create
Artist(const std::string& name, const std::optional<UUID>& MBID = {});
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& UUID = {});
Artist(const std::string& name, const std::optional<core::UUID>& MBID = {});
static pointer create(Session& session, const std::string& name, const std::optional<core::UUID>& UUID = {});
std::string _name;
std::string _sortName;
@@ -136,5 +136,5 @@ namespace Database
Wt::Dbo::collection<Wt::Dbo::ptr<StarredArtist>> _starredArtists; // starred entries for this artist
};
} // namespace Database
} // namespace lms::db
@@ -27,7 +27,7 @@
#include "database/AuthTokenId.hpp"
#include "database/Object.hpp"
namespace Database
namespace lms::db
{
class Session;
@@ -34,8 +34,8 @@
#include "database/TrackId.hpp"
#include "database/Types.hpp"
namespace Database {
namespace lms::db
{
class Track;
class ClusterType;
class Session;
@@ -149,5 +149,5 @@ namespace Database {
Wt::Dbo::collection< Wt::Dbo::ptr<Cluster> > _clusters;
};
} // namespace Database
} // namespace lms::db
+6 -8
View File
@@ -23,10 +23,10 @@
#include <Wt/Dbo/SqlConnectionPool.h>
#include "utils/RecursiveSharedMutex.hpp"
namespace Database {
#include "core/RecursiveSharedMutex.hpp"
namespace lms::db
{
class Session;
class Db
{
@@ -43,7 +43,7 @@ namespace Database {
friend class Session;
RecursiveSharedMutex& getMutex() { return _sharedMutex; }
core::RecursiveSharedMutex& getMutex() { return _sharedMutex; }
Wt::Dbo::SqlConnectionPool& getConnectionPool() { return *_connectionPool; }
class ScopedConnection
@@ -62,13 +62,11 @@ namespace Database {
std::unique_ptr<Wt::Dbo::SqlConnection> _connection;
};
RecursiveSharedMutex _sharedMutex;
core::RecursiveSharedMutex _sharedMutex;
std::unique_ptr<Wt::Dbo::SqlConnectionPool> _connectionPool;
std::mutex _tlsSessionsMutex;
std::vector<std::unique_ptr<Session>> _tlsSessions;
};
} // namespace Database
}
@@ -24,7 +24,7 @@
#include <functional>
#include <Wt/Dbo/ptr.h>
namespace Database
namespace lms::db
{
class IdType
{
@@ -49,7 +49,7 @@ namespace Database
};
#define LMS_DECLARE_IDTYPE(name) \
namespace Database { \
namespace lms::db { \
class name : public IdType \
{ \
public: \
@@ -59,14 +59,14 @@ namespace Database
namespace std \
{ \
template<> \
class hash<Database::name> \
class hash<lms::db::name> \
{ \
public: \
size_t operator()(Database::name id) const \
size_t operator()(lms::db::name id) const \
{ \
return std::hash<Database::name::ValueType>()(id.getValue()); \
return std::hash<lms::db::name::ValueType>()(id.getValue()); \
} \
}; \
} // ns std
} // namespace Database
} // namespace lms::db
@@ -34,9 +34,8 @@
#include "database/Types.hpp"
#include "database/UserId.hpp"
namespace Database
namespace lms::db
{
class Session;
class Track;
class User;
@@ -135,5 +134,5 @@ namespace Database
Wt::Dbo::ptr<Track> _track;
};
} // namespace Database
} // namespace lms::db
@@ -28,7 +28,7 @@
#include "database/MediaLibraryId.hpp"
#include "database/Object.hpp"
namespace Database
namespace lms::db
{
class Session;
@@ -63,11 +63,11 @@ namespace Database
}
private:
friend class ::Database::Session;
friend class Session;
MediaLibrary(const std::filesystem::path& p, std::string_view name);
static pointer create(Session& session, const std::filesystem::path& p = {}, std::string_view name = {});
std::filesystem::path _path;
std::string _name;
};
} // namespace Database
} // namespace lms::db
@@ -24,7 +24,7 @@
#include "database/IdType.hpp"
#include "database/TransactionChecker.hpp"
namespace Database
namespace lms::db
{
template <typename T>
class ObjectPtr
@@ -57,8 +57,8 @@ namespace Database
template <typename T, typename ObjectIdType>
class Object : public Wt::Dbo::Dbo<T>
{
static_assert(std::is_base_of_v<Database::IdType, ObjectIdType>);
static_assert(!std::is_same_v<Database::IdType, ObjectIdType>);
static_assert(std::is_base_of_v<db::IdType, ObjectIdType>);
static_assert(!std::is_same_v<db::IdType, ObjectIdType>);
public:
using pointer = ObjectPtr<T>;
+14 -14
View File
@@ -28,6 +28,8 @@
#include <Wt/WDateTime.h>
#include <Wt/Dbo/Dbo.h>
#include "core/EnumSet.hpp"
#include "core/UUID.hpp"
#include "database/ArtistId.hpp"
#include "database/ClusterId.hpp"
#include "database/MediaLibraryId.hpp"
@@ -36,10 +38,8 @@
#include "database/ReleaseTypeId.hpp"
#include "database/Types.hpp"
#include "database/UserId.hpp"
#include "utils/EnumSet.hpp"
#include "utils/UUID.hpp"
namespace Database
namespace lms::db
{
class Artist;
class Cluster;
@@ -91,8 +91,8 @@ namespace Database
UserId starringUser; // only releases starred by this user
std::optional<FeedbackBackend> feedbackBackend; // and for this backend
ArtistId artist; // only releases that involved this user
EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
EnumSet<TrackArtistLinkType> excludedTrackArtistLinkTypes; // but not for these link types
core::EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
core::EnumSet<TrackArtistLinkType> excludedTrackArtistLinkTypes; // but not for these link types
std::string releaseType; // If set, albums that has this release type
MediaLibraryId mediaLibrary; // If set, releases that has at least a track in this library
@@ -103,7 +103,7 @@ namespace Database
FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; }
FindParameters& setDateRange(const std::optional<DateRange>& _dateRange) { dateRange = _dateRange; return *this; }
FindParameters& setStarringUser(UserId _user, FeedbackBackend _feedbackBackend) { starringUser = _user; feedbackBackend = _feedbackBackend; return *this; }
FindParameters& setArtist(ArtistId _artist, EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}, EnumSet<TrackArtistLinkType> _excludedTrackArtistLinkTypes = {})
FindParameters& setArtist(ArtistId _artist, core::EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}, core::EnumSet<TrackArtistLinkType> _excludedTrackArtistLinkTypes = {})
{
artist = _artist;
trackArtistLinkTypes = _trackArtistLinkTypes;
@@ -119,7 +119,7 @@ namespace Database
// Accessors
static std::size_t getCount(Session& session);
static bool exists(Session& session, ReleaseId id);
static pointer find(Session& session, const UUID& MBID);
static pointer find(Session& session, const core::UUID& MBID);
static std::vector<pointer> find(Session& session, const std::string& name, const std::filesystem::path& releaseDirectory);
static pointer find(Session& session, ReleaseId id);
static RangeResults<pointer> find(Session& session, const FindParameters& parameters);
@@ -145,8 +145,8 @@ namespace Database
// Accessors
std::string_view getName() const { return _name; }
std::string_view getSortName() const { return _sortName; }
std::optional<UUID> getMBID() const { return UUID::fromString(_MBID); }
std::optional<UUID> getGroupMBID() const { return UUID::fromString(_groupMBID); }
std::optional<core::UUID> getMBID() const { return core::UUID::fromString(_MBID); }
std::optional<core::UUID> getGroupMBID() const { return core::UUID::fromString(_groupMBID); }
std::optional<std::size_t> getTotalDisc() const { return _totalDisc; }
std::size_t getDiscCount() const; // may not be total disc (if incomplete for example)
std::vector<DiscInfo> getDiscs() const;
@@ -160,8 +160,8 @@ namespace Database
// Setters
void setName(std::string_view name) { _name = name; }
void setSortName(std::string_view sortName) { _sortName = sortName; }
void setMBID(const std::optional<UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
void setGroupMBID(const std::optional<UUID>& mbid) { _groupMBID = mbid ? mbid->getAsString() : ""; }
void setMBID(const std::optional<core::UUID>& mbid) { _MBID = mbid ? mbid->getAsString() : ""; }
void setGroupMBID(const std::optional<core::UUID>& mbid) { _groupMBID = mbid ? mbid->getAsString() : ""; }
void setTotalDisc(std::optional<int> totalDisc) { _totalDisc = totalDisc; }
void setArtistDisplayName(std::string_view name) { _artistDisplayName = name; }
void clearReleaseTypes();
@@ -188,8 +188,8 @@ namespace Database
private:
friend class Session;
Release(const std::string& name, const std::optional<UUID>& MBID = {});
static pointer create(Session& session, const std::string& name, const std::optional<UUID>& MBID = {});
Release(const std::string& name, const std::optional<core::UUID>& MBID = {});
static pointer create(Session& session, const std::string& name, const std::optional<core::UUID>& MBID = {});
Wt::WDate getDate(bool original) const;
std::optional<int> getYear(bool original) const;
@@ -207,4 +207,4 @@ namespace Database
Wt::Dbo::collection<Wt::Dbo::ptr<ReleaseType>> _releaseTypes; // Release types
};
} // namespace Database
} // namespace lms::db
@@ -22,4 +22,3 @@
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(ReleaseId)
@@ -22,4 +22,3 @@
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(ReleaseTypeId)
@@ -33,7 +33,7 @@
LMS_DECLARE_IDTYPE(ScanSettingsId)
namespace Database
namespace lms::db
{
class Session;
@@ -104,4 +104,4 @@ namespace Database
std::string _artistTagDelimiters;
std::string _defaultTagDelimiters;
};
} // namespace Database
} // namespace lms::db
@@ -22,12 +22,12 @@
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/SqlConnectionPool.h>
#include "utils/ITraceLogger.hpp"
#include "utils/RecursiveSharedMutex.hpp"
#include "core/ITraceLogger.hpp"
#include "core/RecursiveSharedMutex.hpp"
#include "database/Object.hpp"
#include "database/TransactionChecker.hpp"
namespace Database
namespace lms::db
{
class WriteTransaction
{
@@ -36,13 +36,13 @@ namespace Database
private:
friend class Session;
WriteTransaction(RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
WriteTransaction(core::RecursiveSharedMutex& mutex, Wt::Dbo::Session& session);
WriteTransaction(const WriteTransaction&) = delete;
WriteTransaction& operator=(const WriteTransaction&) = delete;
std::unique_lock<RecursiveSharedMutex> _lock;
tracing::ScopedTrace _trace{ "Database", tracing::Level::Detailed, "WriteTransaction" }; // before actual transaction
std::unique_lock<core::RecursiveSharedMutex> _lock;
core::tracing::ScopedTrace _trace{ "Database", core::tracing::Level::Detailed, "WriteTransaction" }; // before actual transaction
Wt::Dbo::Transaction _transaction;
};
@@ -58,7 +58,7 @@ namespace Database
ReadTransaction(const ReadTransaction&) = delete;
ReadTransaction& operator=(const ReadTransaction&) = delete;
tracing::ScopedTrace _trace{ "Database", tracing::Level::Detailed, "ReadTransaction" }; // before actual transaction
core::tracing::ScopedTrace _trace{ "Database", core::tracing::Level::Detailed, "ReadTransaction" }; // before actual transaction
Wt::Dbo::Transaction _transaction;
};
@@ -103,4 +103,4 @@ namespace Database
Db& _db;
Wt::Dbo::Session _session;
};
} // namespace Database
} // namespace lms::db
@@ -28,7 +28,7 @@
#include "database/Types.hpp"
#include "database/UserId.hpp"
namespace Database
namespace lms::db
{
class Artist;
class Session;
@@ -79,5 +79,5 @@ namespace Database
Wt::Dbo::ptr<Artist> _artist;
Wt::Dbo::ptr<User> _user;
};
} // namespace Database
} // namespace lms::db
@@ -22,4 +22,3 @@
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(StarredArtistId)
@@ -28,7 +28,7 @@
#include "database/Types.hpp"
#include "database/UserId.hpp"
namespace Database
namespace lms::db
{
class Release;
class Session;
@@ -79,5 +79,5 @@ namespace Database
Wt::Dbo::ptr<Release> _release;
Wt::Dbo::ptr<User> _user;
};
} // namespace Database
} // namespace lms::db
@@ -22,4 +22,3 @@
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(StarredReleaseId)
@@ -22,14 +22,14 @@
#include <Wt/WDateTime.h>
#include <Wt/Dbo/Dbo.h>
#include "core/EnumSet.hpp"
#include "database/TrackId.hpp"
#include "database/Object.hpp"
#include "database/StarredTrackId.hpp"
#include "database/Types.hpp"
#include "database/UserId.hpp"
#include "utils/EnumSet.hpp"
namespace Database
namespace lms::db
{
class Track;
class Session;
@@ -94,5 +94,5 @@ namespace Database
Wt::Dbo::ptr<Track> _track;
Wt::Dbo::ptr<User> _user;
};
} // namespace Database
} // namespace lms::db
@@ -22,4 +22,3 @@
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(StarredTrackId)
+17 -19
View File
@@ -32,9 +32,8 @@
#include <Wt/Dbo/Dbo.h>
#include <Wt/Dbo/WtSqlTraits.h>
#include "utils/EnumSet.hpp"
#include "utils/UUID.hpp"
#include "core/EnumSet.hpp"
#include "core/UUID.hpp"
#include "database/ArtistId.hpp"
#include "database/ClusterId.hpp"
#include "database/MediaLibraryId.hpp"
@@ -45,8 +44,8 @@
#include "database/Types.hpp"
#include "database/UserId.hpp"
namespace Database {
namespace lms::db
{
class Artist;
class Cluster;
class ClusterType;
@@ -72,7 +71,7 @@ namespace Database {
std::optional<FeedbackBackend> feedbackBackend; // and for this feedback backend
ArtistId artist; // only tracks that involve this artist
std::string artistName; // only tracks that involve this artist name
EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
core::EnumSet<TrackArtistLinkType> trackArtistLinkTypes; // and for these link types
bool nonRelease{}; // only tracks that do not belong to a release
ReleaseId release; // matching this release
std::string releaseName; // matching this release name
@@ -88,8 +87,8 @@ namespace Database {
FindParameters& setRange(std::optional<Range> _range) { range = _range; return *this; }
FindParameters& setWrittenAfter(const Wt::WDateTime& _after) { writtenAfter = _after; return *this; }
FindParameters& setStarringUser(UserId _user, FeedbackBackend _feedbackBackend) { starringUser = _user; feedbackBackend = _feedbackBackend; return *this; }
FindParameters& setArtist(ArtistId _artist, EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}) { artist = _artist; trackArtistLinkTypes = _trackArtistLinkTypes; return *this; }
FindParameters& setArtistName(std::string_view _artistName, EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}) { artistName = _artistName; trackArtistLinkTypes = _trackArtistLinkTypes; return *this; }
FindParameters& setArtist(ArtistId _artist, core::EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}) { artist = _artist; trackArtistLinkTypes = _trackArtistLinkTypes; return *this; }
FindParameters& setArtistName(std::string_view _artistName, core::EnumSet<TrackArtistLinkType> _trackArtistLinkTypes = {}) { artistName = _artistName; trackArtistLinkTypes = _trackArtistLinkTypes; return *this; }
FindParameters& setNonRelease(bool _nonRelease) { nonRelease = _nonRelease; return *this; }
FindParameters& setRelease(ReleaseId _release) { release = _release; return *this; }
FindParameters& setReleaseName(std::string_view _releaseName) { releaseName = _releaseName; return *this; }
@@ -112,8 +111,8 @@ namespace Database {
static pointer findByPath(Session& session, const std::filesystem::path& p);
static pointer find(Session& session, TrackId id);
static bool exists(Session& session, TrackId id);
static std::vector<pointer> findByRecordingMBID(Session& session, const UUID& MBID);
static std::vector<pointer> findByMBID(Session& session, const UUID& MBID);
static std::vector<pointer> findByRecordingMBID(Session& session, const core::UUID& MBID);
static std::vector<pointer> findByMBID(Session& session, const core::UUID& MBID);
static RangeResults<TrackId> findSimilarTrackIds(Session& session, const std::vector<TrackId>& trackIds, std::optional<Range> range = std::nullopt);
static RangeResults<TrackId> findIds(Session& session, const FindParameters& parameters);
@@ -140,8 +139,8 @@ namespace Database {
void setOriginalDate(const Wt::WDate& date) { _originalDate = date; }
void setOriginalYear(std::optional<int> year) { _originalYear = year; }
void setHasCover(bool hasCover) { _hasCover = hasCover; }
void setTrackMBID(const std::optional<UUID>& MBID) { _trackMBID = MBID ? MBID->getAsString() : ""; }
void setRecordingMBID(const std::optional<UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
void setTrackMBID(const std::optional<core::UUID>& MBID) { _trackMBID = MBID ? MBID->getAsString() : ""; }
void setRecordingMBID(const std::optional<core::UUID>& MBID) { _recordingMBID = MBID ? MBID->getAsString() : ""; }
void setCopyright(const std::string& copyright) { _copyright = std::string(copyright, 0, _maxCopyrightLength); }
void setCopyrightURL(const std::string& copyrightURL) { _copyrightURL = std::string(copyrightURL, 0, _maxCopyrightURLLength); }
void setTrackReplayGain(std::optional<float> replayGain) { _trackReplayGain = replayGain; }
@@ -170,16 +169,16 @@ namespace Database {
Wt::WDateTime getLastWriteTime() const { return _fileLastWrite; }
Wt::WDateTime getAddedTime() const { return _fileAdded; }
bool hasCover() const { return _hasCover; }
std::optional<UUID> getTrackMBID() const { return UUID::fromString(_trackMBID); }
std::optional<UUID> getRecordingMBID() const { return UUID::fromString(_recordingMBID); }
std::optional<core::UUID> getTrackMBID() const { return core::UUID::fromString(_trackMBID); }
std::optional<core::UUID> getRecordingMBID() const { return core::UUID::fromString(_recordingMBID); }
std::optional<std::string> getCopyright() const;
std::optional<std::string> getCopyrightURL() const;
std::optional<float> getTrackReplayGain() const { return _trackReplayGain; }
std::optional<float> getReleaseReplayGain() const { return _releaseReplayGain; }
std::string_view getArtistDisplayName() const { return _artistDisplayName; }
// no artistLinkTypes means get all
std::vector<ObjectPtr<Artist>> getArtists(EnumSet<TrackArtistLinkType> artistLinkTypes) const; // no type means all
std::vector<ArtistId> getArtistIds(EnumSet<TrackArtistLinkType> artistLinkTypes) const; // no type means all
std::vector<ObjectPtr<Artist>> getArtists(core::EnumSet<TrackArtistLinkType> artistLinkTypes) const; // no type means all
std::vector<ArtistId> getArtistIds(core::EnumSet<TrackArtistLinkType> artistLinkTypes) const; // no type means all
std::vector<ObjectPtr<TrackArtistLink>> getArtistLinks() const;
ObjectPtr<Release> getRelease() const { return _release; }
std::vector<ObjectPtr<Cluster>> getClusters() const;
@@ -221,7 +220,7 @@ namespace Database {
}
private:
friend class ::Database::Session;
friend class Session;
Track(const std::filesystem::path& p);
static pointer create(Session& session, const std::filesystem::path& p);
@@ -268,7 +267,6 @@ namespace Database {
};
std::ostream& operator<<(std::ostream& os, const TrackInfo& trackInfo);
}
} // namespace database
} // namespace lms::db
@@ -25,17 +25,17 @@
#include <Wt/Dbo/Dbo.h>
#include "core/EnumSet.hpp"
#include "database/ArtistId.hpp"
#include "database/IdType.hpp"
#include "database/Object.hpp"
#include "database/ReleaseId.hpp"
#include "database/TrackId.hpp"
#include "database/Types.hpp"
#include "utils/EnumSet.hpp"
LMS_DECLARE_IDTYPE(TrackArtistLinkId)
namespace Database
namespace lms::db
{
class Artist;
class Session;
@@ -65,8 +65,8 @@ namespace Database
static RangeResults<TrackArtistLinkId> find(Session& session, const FindParameters& parameters);
static pointer find(Session& session, TrackArtistLinkId linkId);
static pointer create(Session& session, ObjectPtr<Track> track, ObjectPtr<Artist> artist, TrackArtistLinkType type, std::string_view subType = {});
static EnumSet<TrackArtistLinkType> findUsedTypes(Session& session);
static EnumSet<TrackArtistLinkType> findUsedTypes(Session& session, ArtistId _artist);
static core::EnumSet<TrackArtistLinkType> findUsedTypes(Session& session);
static core::EnumSet<TrackArtistLinkType> findUsedTypes(Session& session, ArtistId _artist);
ObjectPtr<Track> getTrack() const { return _track; }
ObjectPtr<Artist> getArtist() const { return _artist; }
@@ -32,56 +32,54 @@
LMS_DECLARE_IDTYPE(TrackBookmarkId)
namespace Database {
class Session;
class Track;
class User;
class TrackBookmark final : public Object<TrackBookmark, TrackBookmarkId>
namespace lms::db
{
public:
TrackBookmark () = default;
class Session;
class Track;
class User;
// Find utility functions
static std::size_t getCount(Session& session);
static pointer find(Session& session, TrackBookmarkId id);
static RangeResults<TrackBookmarkId> find(Session& session, UserId userId, std::optional<Range> range = std::nullopt);
static pointer find(Session& session, UserId userId, TrackId trackId);
class TrackBookmark final : public Object<TrackBookmark, TrackBookmarkId>
{
public:
TrackBookmark() = default;
// Setters
void setOffset(std::chrono::milliseconds offset) { _offset = offset; }
void setComment(std::string_view comment) { _comment = comment; }
// Find utility functions
static std::size_t getCount(Session& session);
static pointer find(Session& session, TrackBookmarkId id);
static RangeResults<TrackBookmarkId> find(Session& session, UserId userId, std::optional<Range> range = std::nullopt);
static pointer find(Session& session, UserId userId, TrackId trackId);
// Getters
std::chrono::milliseconds getOffset() const { return _offset; }
std::string_view getComment() const { return _comment; }
ObjectPtr<Track> getTrack() const { return _track; }
ObjectPtr<User> getUser() const { return _user; }
// Setters
void setOffset(std::chrono::milliseconds offset) { _offset = offset; }
void setComment(std::string_view comment) { _comment = comment; }
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _offset, "offset");
Wt::Dbo::field(a, _comment, "comment");
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
}
// Getters
std::chrono::milliseconds getOffset() const { return _offset; }
std::string_view getComment() const { return _comment; }
ObjectPtr<Track> getTrack() const { return _track; }
ObjectPtr<User> getUser() const { return _user; }
private:
friend class Session;
TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track);
static pointer create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track);
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _offset, "offset");
Wt::Dbo::field(a, _comment, "comment");
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
Wt::Dbo::belongsTo(a, _user, "user", Wt::Dbo::OnDeleteCascade);
}
static const std::size_t _maxCommentLength = 128;
private:
friend class Session;
TrackBookmark(ObjectPtr<User> user, ObjectPtr<Track> track);
static pointer create(Session& session, ObjectPtr<User> user, ObjectPtr<Track> track);
std::chrono::duration<int, std::milli> _offset;
std::string _comment;
static const std::size_t _maxCommentLength = 128;
Wt::Dbo::ptr<User> _user;
Wt::Dbo::ptr<Track> _track;
};
} // namespace database
std::chrono::duration<int, std::milli> _offset;
std::string _comment;
Wt::Dbo::ptr<User> _user;
Wt::Dbo::ptr<Track> _track;
};
} // namespace lms::db
@@ -34,49 +34,46 @@
LMS_DECLARE_IDTYPE(TrackFeaturesId)
namespace Database {
class Session;
class Track;
using FeatureName = std::string;
using FeatureValues = std::vector<double>;
using FeatureValuesMap = std::unordered_map<FeatureName, FeatureValues>;
class TrackFeatures final : public Object<TrackFeatures, TrackFeaturesId>
namespace lms::db
{
public:
TrackFeatures() = default;
class Session;
class Track;
// Find utilities
static std::size_t getCount(Session& session);
static pointer find(Session& session, TrackFeaturesId id);
static pointer find(Session& session, TrackId trackId);
static RangeResults<TrackFeaturesId> find(Session& session, std::optional<Range> range = std::nullopt);
using FeatureName = std::string;
using FeatureValues = std::vector<double>;
using FeatureValuesMap = std::unordered_map<FeatureName, FeatureValues>;
FeatureValues getFeatureValues(const FeatureName& feature) const;
FeatureValuesMap getFeatureValuesMap(const std::unordered_set<FeatureName>& featureNames) const;
class TrackFeatures final : public Object<TrackFeatures, TrackFeaturesId>
{
public:
TrackFeatures() = default;
// Accessors
Wt::Dbo::ptr<Track> getTrack() const { return _track; }
// Find utilities
static std::size_t getCount(Session& session);
static pointer find(Session& session, TrackFeaturesId id);
static pointer find(Session& session, TrackId trackId);
static RangeResults<TrackFeaturesId> find(Session& session, std::optional<Range> range = std::nullopt);
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _data, "data");
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
}
FeatureValues getFeatureValues(const FeatureName& feature) const;
FeatureValuesMap getFeatureValuesMap(const std::unordered_set<FeatureName>& featureNames) const;
private:
friend class Session;
TrackFeatures(ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
static pointer create(Session& session, ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
// Accessors
Wt::Dbo::ptr<Track> getTrack() const { return _track; }
std::string _data;
Wt::Dbo::ptr<Track> _track;
};
template<class Action>
void persist(Action& a)
{
Wt::Dbo::field(a, _data, "data");
Wt::Dbo::belongsTo(a, _track, "track", Wt::Dbo::OnDeleteCascade);
}
private:
friend class Session;
TrackFeatures(ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
static pointer create(Session& session, ObjectPtr<Track> track, const std::string& jsonEncodedFeatures);
} // namespace database
std::string _data;
Wt::Dbo::ptr<Track> _track;
};
} // namespace lms::db
@@ -22,4 +22,3 @@
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(TrackId)
@@ -34,8 +34,8 @@
#include "database/Types.hpp"
#include "database/UserId.hpp"
namespace Database {
namespace lms::db
{
class Artist;
class Cluster;
class ClusterType;
@@ -168,5 +168,5 @@ namespace Database {
Wt::Dbo::ptr<TrackList> _tracklist;
};
} // namespace Database
} // namespace lms::db
@@ -23,4 +23,3 @@
LMS_DECLARE_IDTYPE(TrackListId)
LMS_DECLARE_IDTYPE(TrackListEntryId)
@@ -22,7 +22,7 @@
#include <vector>
#include <Wt/Dbo/Session.h>
namespace Database
namespace lms::db
{
class Session;
+1 -1
View File
@@ -24,7 +24,7 @@
#include <functional>
#include <Wt/WDate.h>
namespace Database
namespace lms::db
{
// Caution: do not change enum values if they are set!
+5 -5
View File
@@ -26,13 +26,13 @@
#include <Wt/Dbo/Dbo.h>
#include <Wt/WDateTime.h>
#include "core/UUID.hpp"
#include "database/Object.hpp"
#include "database/Types.hpp"
#include "database/UserId.hpp"
#include "utils/UUID.hpp"
namespace Database {
namespace lms::db
{
class AuthToken;
class Session;
@@ -95,7 +95,7 @@ namespace Database {
void setSubsonicArtistListMode(SubsonicArtistListMode mode) { _subsonicArtistListMode = mode; }
void setFeedbackBackend(FeedbackBackend feedbackBackend) { _feedbackBackend = feedbackBackend; }
void setScrobblingBackend(ScrobblingBackend scrobblingBackend) { _scrobblingBackend = scrobblingBackend; }
void setListenBrainzToken(const std::optional<UUID>& MBID) { _listenbrainzToken = MBID ? MBID->getAsString() : ""; }
void setListenBrainzToken(const std::optional<core::UUID>& MBID) { _listenbrainzToken = MBID ? MBID->getAsString() : ""; }
// read
bool isAdmin() const { return _type == UserType::ADMIN; }
@@ -111,7 +111,7 @@ namespace Database {
SubsonicArtistListMode getSubsonicArtistListMode() const { return _subsonicArtistListMode; }
FeedbackBackend getFeedbackBackend() const { return _feedbackBackend; }
ScrobblingBackend getScrobblingBackend() const { return _scrobblingBackend; }
std::optional<UUID> getListenBrainzToken() const { return UUID::fromString(_listenbrainzToken); }
std::optional<core::UUID> getListenBrainzToken() const { return core::UUID::fromString(_listenbrainzToken); }
template<class Action>
void persist(Action& a)
@@ -22,4 +22,3 @@
#include "database/IdType.hpp"
LMS_DECLARE_IDTYPE(UserId)
+425 -424
View File
@@ -19,472 +19,473 @@
#include "Common.hpp"
using namespace Database;
TEST_F(DatabaseFixture, Artist)
namespace lms::db::tests
{
TEST_F(DatabaseFixture, Artist)
{
auto transaction{ session.createReadTransaction() };
EXPECT_FALSE(Artist::exists(session, 35));
EXPECT_FALSE(Artist::exists(session, 0));
EXPECT_FALSE(Artist::exists(session, 1));
EXPECT_EQ(Artist::getCount(session), 0);
}
ScopedArtist artist{ session, "MyArtist" };
{
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(artist.get());
EXPECT_FALSE(!artist.get());
EXPECT_EQ(artist.get()->getId(), artist.getId());
EXPECT_TRUE(Artist::exists(session, artist.getId()));
EXPECT_EQ(Artist::getCount(session), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters {}) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
artists = Artist::findOrphanIds(session);
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::find(session, Artist::FindParameters {}) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front()->getId(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
bool visited{};
Artist::find(session, Artist::FindParameters{}, [&](const Artist::pointer& a)
{
visited = true;
EXPECT_EQ(a->getId(), artist.getId());
});
EXPECT_TRUE(visited);
}
}
TEST_F(DatabaseFixture, Artist_singleTrack)
{
ScopedTrack track{ session, "MyTrack" };
ScopedArtist artist{ session, "MyArtist" };
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setName("MyTrackName");
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findOrphanIds(session).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtists({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front()->getId(), artist.getId());
ASSERT_EQ(track->getArtistLinks().size(), 1);
auto artistLink{ track->getArtistLinks().front() };
EXPECT_EQ(artistLink->getTrack()->getId(), track.getId());
EXPECT_EQ(artistLink->getArtist()->getId(), artist.getId());
ASSERT_EQ(track->getArtists({ TrackArtistLinkType::Artist }).size(), 1);
EXPECT_EQ(track->getArtists({ TrackArtistLinkType::ReleaseArtist }).size(), 0);
EXPECT_EQ(track->getArtists({}).size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtistIds({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front(), artist.getId());
ASSERT_EQ(track->getArtistIds({ TrackArtistLinkType::Artist }).size(), 1);
EXPECT_EQ(track->getArtistIds({ TrackArtistLinkType::ReleaseArtist }).size(), 0);
EXPECT_EQ(track->getArtistIds({}).size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setArtistName("MyArtist")) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setArtistName("MyArtistFoo")) };
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackNameFoo").setArtistName("MyArtist")) };
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setTrack(track->getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
}
TEST_F(DatabaseFixture, Artist_singleTrack_mediaLibrary)
{
ScopedTrack track{ session, "MyTrack" };
ScopedArtist artist{ session, "MyArtist" };
ScopedMediaLibrary library{ session };
ScopedMediaLibrary otherLibrary{ session };
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setName("MyTrackName");
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
track.get().modify()->setMediaLibrary(library.get());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setTrack(track->getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setMediaLibrary(library->getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setMediaLibrary(otherLibrary->getId())) };
EXPECT_EQ(artists.results.size(), 0);
}
}
TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles)
{
ScopedTrack track{ session, "MyTrack" };
ScopedArtist artist{ session, "MyArtist" };
{
auto transaction{ session.createWriteTransaction() };
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::ReleaseArtist);
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Writer);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findOrphanIds(session, Range{}).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Artist)).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::ReleaseArtist)).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Writer)).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Composer)).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtists({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front()->getId(), artist.getId());
artists = track->getArtists({ TrackArtistLinkType::ReleaseArtist });
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front()->getId(), artist.getId());
EXPECT_EQ(track->getArtistLinks().size(), 3);
auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist.getId())) };
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::ReleaseArtist }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::Artist }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::Writer }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::Composer }));
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
EnumSet<TrackArtistLinkType> types{ TrackArtistLink::findUsedTypes(session, artist.getId()) };
EXPECT_TRUE(types.contains(TrackArtistLinkType::ReleaseArtist));
EXPECT_TRUE(types.contains(TrackArtistLinkType::Artist));
EXPECT_TRUE(types.contains(TrackArtistLinkType::Writer));
EXPECT_FALSE(types.contains(TrackArtistLinkType::Composer));
}
}
TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists)
{
ScopedTrack track{ session, "track" };
ScopedArtist artist1{ session, "artist1" };
ScopedArtist artist2{ session, "artist2" };
ASSERT_NE(artist1.getId(), artist2.getId());
{
auto transaction{ session.createWriteTransaction() };
TrackArtistLink::create(session, track.get(), artist1.get(), TrackArtistLinkType::Artist);
TrackArtistLink::create(session, track.get(), artist2.get(), TrackArtistLinkType::Artist);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findOrphanIds(session).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtists({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 2);
EXPECT_TRUE((artists[0]->getId() == artist1.getId() && artists[1]->getId() == artist2.getId())
|| (artists[0]->getId() == artist2.getId() && artists[1]->getId() == artist1.getId()));
EXPECT_EQ(track->getArtists({}).size(), 2);
EXPECT_EQ(track->getArtists({ TrackArtistLinkType::Artist }).size(), 2);
EXPECT_EQ(track->getArtists({ TrackArtistLinkType::ReleaseArtist }).size(), 0);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}).results.size(), 2);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::Random)).results.size(), 2);
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist1->getId())) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track->getId());
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist2->getId()));
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track->getId());
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist1->getId(), { TrackArtistLinkType::ReleaseArtist }));
EXPECT_EQ(tracks.results.size(), 0);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist1->getId(), { TrackArtistLinkType::Artist }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist2->getId(), { TrackArtistLinkType::ReleaseArtist }));
EXPECT_EQ(tracks.results.size(), 0);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist2->getId(), { TrackArtistLinkType::Artist }));
EXPECT_EQ(tracks.results.size(), 1);
}
}
TEST_F(DatabaseFixture, Artist_findByName)
{
ScopedArtist artist{ session, "AAA" };
ScopedTrack track{ session, "MyTrack" }; // filters does not work on orphans
{
auto transaction{ session.createWriteTransaction() };
artist.get().modify()->setSortName("ZZZ");
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setKeywords({ "N" })).results.size(), 0);
const auto artistsByAAA{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"A"})) };
ASSERT_EQ(artistsByAAA.results.size(), 1);
EXPECT_EQ(artistsByAAA.results.front(), artist.getId());
const auto artistsByZZZ{ Artist::Artist::findIds(session, Artist::FindParameters {}.setKeywords({"Z"})) };
ASSERT_EQ(artistsByZZZ.results.size(), 1);
EXPECT_EQ(artistsByZZZ.results.front(), artist.getId());
EXPECT_EQ(Artist::find(session, "NNN").size(), 0);
EXPECT_EQ(Artist::find(session, "AAA").size(), 1);
}
}
TEST_F(DatabaseFixture, Artist_findByNameEscaped)
{
ScopedArtist artist1{ session, R"(MyArtist%)" };
ScopedArtist artist2{ session, R"(%MyArtist)" };
ScopedArtist artist3{ session, R"(%_MyArtist)" };
ScopedArtist artist4{ session, R"(MyArtist%foo)" };
ScopedArtist artist5{ session, R"(foo%MyArtist)" };
ScopedArtist artist6{ session, R"(%AMyArtist)" };
{
auto transaction{ session.createReadTransaction() };
{
const auto artists{ Artist::find(session, R"(MyArtist%)") };
ASSERT_TRUE(artists.size() == 1);
EXPECT_EQ(artists.front()->getId(), artist1.getId());
EXPECT_EQ(Artist::find(session, R"(MyArtistFoo)").size(), 0);
auto transaction{ session.createReadTransaction() };
EXPECT_FALSE(Artist::exists(session, 35));
EXPECT_FALSE(Artist::exists(session, 0));
EXPECT_FALSE(Artist::exists(session, 1));
EXPECT_EQ(Artist::getCount(session), 0);
}
{
const auto artists{ Artist::find(session, R"(%MyArtist)") };
ASSERT_TRUE(artists.size() == 1);
EXPECT_EQ(artists.front()->getId(), artist2.getId());
EXPECT_EQ(Artist::find(session, R"(FooMyArtist)").size(), 0);
}
{
const auto artists{ Artist::find(session, R"(%_MyArtist)") };
ASSERT_TRUE(artists.size() == 1);
ASSERT_EQ(artists.front()->getId(), artist3.getId());
EXPECT_EQ(Artist::find(session, R"(%CMyArtist)").size(), 0);
}
}
{
auto transaction{ session.createReadTransaction() };
ScopedArtist artist{ session, "MyArtist" };
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"MyArtist"})) };
EXPECT_EQ(artists.results.size(), 6);
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(artist.get());
EXPECT_FALSE(!artist.get());
EXPECT_EQ(artist.get()->getId(), artist.getId());
EXPECT_TRUE(Artist::exists(session, artist.getId()));
EXPECT_EQ(Artist::getCount(session), 1);
}
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"MyArtist%"}).setSortMethod(ArtistSortMethod::ByName)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], artist1.getId());
EXPECT_EQ(artists.results[1], artist4.getId());
}
auto transaction{ session.createReadTransaction() };
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"%MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], artist2.getId());
EXPECT_EQ(artists.results[1], artist5.getId());
}
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"_MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) };
auto artists{ Artist::findIds(session, Artist::FindParameters {}) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results[0], artist3.getId());
EXPECT_EQ(artists.results.front(), artist.getId());
artists = Artist::findOrphanIds(session);
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::find(session, Artist::FindParameters {}) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front()->getId(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
bool visited{};
Artist::find(session, Artist::FindParameters{}, [&](const Artist::pointer& a)
{
visited = true;
EXPECT_EQ(a->getId(), artist.getId());
});
EXPECT_TRUE(visited);
}
}
}
TEST_F(DatabaseFixture, Artist_sortMethod)
{
ScopedArtist artistA{ session, "artistA" };
ScopedArtist artistB{ session, "artistB" };
TEST_F(DatabaseFixture, Artist_singleTrack)
{
auto transaction{ session.createWriteTransaction() };
ScopedTrack track{ session, "MyTrack" };
ScopedArtist artist{ session, "MyArtist" };
artistA.get().modify()->setSortName("sortNameB");
artistB.get().modify()->setSortName("sortNameA");
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setName("MyTrackName");
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findOrphanIds(session).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtists({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front()->getId(), artist.getId());
ASSERT_EQ(track->getArtistLinks().size(), 1);
auto artistLink{ track->getArtistLinks().front() };
EXPECT_EQ(artistLink->getTrack()->getId(), track.getId());
EXPECT_EQ(artistLink->getArtist()->getId(), artist.getId());
ASSERT_EQ(track->getArtists({ TrackArtistLinkType::Artist }).size(), 1);
EXPECT_EQ(track->getArtists({ TrackArtistLinkType::ReleaseArtist }).size(), 0);
EXPECT_EQ(track->getArtists({}).size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtistIds({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front(), artist.getId());
ASSERT_EQ(track->getArtistIds({ TrackArtistLinkType::Artist }).size(), 1);
EXPECT_EQ(track->getArtistIds({ TrackArtistLinkType::ReleaseArtist }).size(), 0);
EXPECT_EQ(track->getArtistIds({}).size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setArtistName("MyArtist")) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackName").setArtistName("MyArtistFoo")) };
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters{}.setName("MyTrackNameFoo").setArtistName("MyArtist")) };
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setTrack(track->getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
}
TEST_F(DatabaseFixture, Artist_singleTrack_mediaLibrary)
{
auto transaction{ session.createReadTransaction() };
ScopedTrack track{ session, "MyTrack" };
ScopedArtist artist{ session, "MyArtist" };
ScopedMediaLibrary library{ session };
ScopedMediaLibrary otherLibrary{ session };
auto allArtistsByName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::ByName)) };
auto allArtistsBySortName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) };
{
auto transaction{ session.createWriteTransaction() };
ASSERT_EQ(allArtistsByName.results.size(), 2);
EXPECT_EQ(allArtistsByName.results.front(), artistA.getId());
EXPECT_EQ(allArtistsByName.results.back(), artistB.getId());
ASSERT_EQ(allArtistsBySortName.results.size(), 2);
EXPECT_EQ(allArtistsBySortName.results.front(), artistB.getId());
EXPECT_EQ(allArtistsBySortName.results.back(), artistA.getId());
}
}
TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
{
ScopedArtist artist{ session, "artist" };
ScopedTrack track1{ session, "MyTrack1" };
ScopedTrack track2{ session, "MyTrack2" };
ScopedRelease release{ session, "MyRelease" };
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setNonRelease(true).setArtist(artist->getId())) };
EXPECT_EQ(tracks.results.size(), 0);
track.get().modify()->setName("MyTrackName");
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
track.get().modify()->setMediaLibrary(library.get());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setTrack(track->getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setMediaLibrary(library->getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters{}.setMediaLibrary(otherLibrary->getId())) };
EXPECT_EQ(artists.results.size(), 0);
}
}
TEST_F(DatabaseFixture, Artist_singleTracktMultiRoles)
{
auto transaction{ session.createWriteTransaction() };
ScopedTrack track{ session, "MyTrack" };
ScopedArtist artist{ session, "MyArtist" };
{
auto transaction{ session.createWriteTransaction() };
TrackArtistLink::create(session, track1.get(), artist.get(), TrackArtistLinkType::Artist);
TrackArtistLink::create(session, track2.get(), artist.get(), TrackArtistLinkType::Artist);
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::ReleaseArtist);
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Writer);
}
track1.get().modify()->setRelease(release.get());
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findOrphanIds(session, Range{}).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Artist)).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::ReleaseArtist)).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Writer)).results.size(), 1);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setLinkType(TrackArtistLinkType::Composer)).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtists({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front()->getId(), artist.getId());
artists = track->getArtists({ TrackArtistLinkType::ReleaseArtist });
ASSERT_EQ(artists.size(), 1);
EXPECT_EQ(artists.front()->getId(), artist.getId());
EXPECT_EQ(track->getArtistLinks().size(), 3);
auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist.getId())) };
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::ReleaseArtist }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::Artist }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::Writer }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist.getId(), { TrackArtistLinkType::Composer }));
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
core::EnumSet<TrackArtistLinkType> types{ TrackArtistLink::findUsedTypes(session, artist.getId()) };
EXPECT_TRUE(types.contains(TrackArtistLinkType::ReleaseArtist));
EXPECT_TRUE(types.contains(TrackArtistLinkType::Artist));
EXPECT_TRUE(types.contains(TrackArtistLinkType::Writer));
EXPECT_FALSE(types.contains(TrackArtistLinkType::Composer));
}
}
TEST_F(DatabaseFixture, Artist_singleTrackMultiArtists)
{
auto transaction{ session.createReadTransaction() };
ScopedTrack track{ session, "track" };
ScopedArtist artist1{ session, "artist1" };
ScopedArtist artist2{ session, "artist2" };
ASSERT_NE(artist1.getId(), artist2.getId());
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist.getId()).setNonRelease(true)) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track2.getId());
}
}
{
auto transaction{ session.createWriteTransaction() };
TEST_F(DatabaseFixture, Artist_findByRelease)
{
ScopedArtist artist{ session, "artist" };
ScopedTrack track{ session, "MyTrack" };
ScopedRelease release{ session, "MyRelease" };
TrackArtistLink::create(session, track.get(), artist1.get(), TrackArtistLinkType::Artist);
TrackArtistLink::create(session, track.get(), artist2.get(), TrackArtistLinkType::Artist);
}
{
auto transaction{ session.createReadTransaction() };
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) };
EXPECT_EQ(artists.results.size(), 0);
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findOrphanIds(session).results.size(), 0);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ track->getArtists({TrackArtistLinkType::Artist}) };
ASSERT_EQ(artists.size(), 2);
EXPECT_TRUE((artists[0]->getId() == artist1.getId() && artists[1]->getId() == artist2.getId())
|| (artists[0]->getId() == artist2.getId() && artists[1]->getId() == artist1.getId()));
EXPECT_EQ(track->getArtists({}).size(), 2);
EXPECT_EQ(track->getArtists({ TrackArtistLinkType::Artist }).size(), 2);
EXPECT_EQ(track->getArtists({ TrackArtistLinkType::ReleaseArtist }).size(), 0);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}).results.size(), 2);
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setSortMethod(ArtistSortMethod::Random)).results.size(), 2);
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist1->getId())) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track->getId());
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist2->getId()));
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track->getId());
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist1->getId(), { TrackArtistLinkType::ReleaseArtist }));
EXPECT_EQ(tracks.results.size(), 0);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist1->getId(), { TrackArtistLinkType::Artist }));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist2->getId(), { TrackArtistLinkType::ReleaseArtist }));
EXPECT_EQ(tracks.results.size(), 0);
tracks = Track::findIds(session, Track::FindParameters{}.setArtist(artist2->getId(), { TrackArtistLinkType::Artist }));
EXPECT_EQ(tracks.results.size(), 1);
}
}
TEST_F(DatabaseFixture, Artist_findByName)
{
auto transaction{ session.createWriteTransaction() };
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
ScopedArtist artist{ session, "AAA" };
ScopedTrack track{ session, "MyTrack" }; // filters does not work on orphans
{
auto transaction{ session.createWriteTransaction() };
artist.get().modify()->setSortName("ZZZ");
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Artist::findIds(session, Artist::FindParameters{}.setKeywords({ "N" })).results.size(), 0);
const auto artistsByAAA{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"A"})) };
ASSERT_EQ(artistsByAAA.results.size(), 1);
EXPECT_EQ(artistsByAAA.results.front(), artist.getId());
const auto artistsByZZZ{ Artist::Artist::findIds(session, Artist::FindParameters {}.setKeywords({"Z"})) };
ASSERT_EQ(artistsByZZZ.results.size(), 1);
EXPECT_EQ(artistsByZZZ.results.front(), artist.getId());
EXPECT_EQ(Artist::find(session, "NNN").size(), 0);
EXPECT_EQ(Artist::find(session, "AAA").size(), 1);
}
}
TEST_F(DatabaseFixture, Artist_findByNameEscaped)
{
auto transaction{ session.createReadTransaction() };
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) };
EXPECT_EQ(artists.results.size(), 0);
ScopedArtist artist1{ session, R"(MyArtist%)" };
ScopedArtist artist2{ session, R"(%MyArtist)" };
ScopedArtist artist3{ session, R"(%_MyArtist)" };
ScopedArtist artist4{ session, R"(MyArtist%foo)" };
ScopedArtist artist5{ session, R"(foo%MyArtist)" };
ScopedArtist artist6{ session, R"(%AMyArtist)" };
{
auto transaction{ session.createReadTransaction() };
{
const auto artists{ Artist::find(session, R"(MyArtist%)") };
ASSERT_TRUE(artists.size() == 1);
EXPECT_EQ(artists.front()->getId(), artist1.getId());
EXPECT_EQ(Artist::find(session, R"(MyArtistFoo)").size(), 0);
}
{
const auto artists{ Artist::find(session, R"(%MyArtist)") };
ASSERT_TRUE(artists.size() == 1);
EXPECT_EQ(artists.front()->getId(), artist2.getId());
EXPECT_EQ(Artist::find(session, R"(FooMyArtist)").size(), 0);
}
{
const auto artists{ Artist::find(session, R"(%_MyArtist)") };
ASSERT_TRUE(artists.size() == 1);
ASSERT_EQ(artists.front()->getId(), artist3.getId());
EXPECT_EQ(Artist::find(session, R"(%CMyArtist)").size(), 0);
}
}
{
auto transaction{ session.createReadTransaction() };
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"MyArtist"})) };
EXPECT_EQ(artists.results.size(), 6);
}
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"MyArtist%"}).setSortMethod(ArtistSortMethod::ByName)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], artist1.getId());
EXPECT_EQ(artists.results[1], artist4.getId());
}
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"%MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], artist2.getId());
EXPECT_EQ(artists.results[1], artist5.getId());
}
{
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setKeywords({"_MyArtist"}).setSortMethod(ArtistSortMethod::ByName)) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results[0], artist3.getId());
}
}
}
TEST_F(DatabaseFixture, Artist_sortMethod)
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setRelease(release.get());
ScopedArtist artistA{ session, "artistA" };
ScopedArtist artistB{ session, "artistB" };
{
auto transaction{ session.createWriteTransaction() };
artistA.get().modify()->setSortName("sortNameB");
artistB.get().modify()->setSortName("sortNameA");
}
{
auto transaction{ session.createReadTransaction() };
auto allArtistsByName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::ByName)) };
auto allArtistsBySortName{ Artist::findIds(session, Artist::FindParameters {}.setSortMethod(ArtistSortMethod::BySortName)) };
ASSERT_EQ(allArtistsByName.results.size(), 2);
EXPECT_EQ(allArtistsByName.results.front(), artistA.getId());
EXPECT_EQ(allArtistsByName.results.back(), artistB.getId());
ASSERT_EQ(allArtistsBySortName.results.size(), 2);
EXPECT_EQ(allArtistsBySortName.results.front(), artistB.getId());
EXPECT_EQ(allArtistsBySortName.results.back(), artistA.getId());
}
}
TEST_F(DatabaseFixture, Artist_nonReleaseTracks)
{
auto transaction{ session.createReadTransaction() };
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
ScopedArtist artist{ session, "artist" };
ScopedTrack track1{ session, "MyTrack1" };
ScopedTrack track2{ session, "MyTrack2" };
ScopedRelease release{ session, "MyRelease" };
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setNonRelease(true).setArtist(artist->getId())) };
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
TrackArtistLink::create(session, track1.get(), artist.get(), TrackArtistLinkType::Artist);
TrackArtistLink::create(session, track2.get(), artist.get(), TrackArtistLinkType::Artist);
track1.get().modify()->setRelease(release.get());
}
{
auto transaction{ session.createReadTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setArtist(artist.getId()).setNonRelease(true)) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track2.getId());
}
}
}
TEST_F(DatabaseFixture, Artist_findByRelease)
{
ScopedArtist artist{ session, "artist" };
ScopedTrack track{ session, "MyTrack" };
ScopedRelease release{ session, "MyRelease" };
{
auto transaction{ session.createReadTransaction() };
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) };
EXPECT_EQ(artists.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist);
}
{
auto transaction{ session.createReadTransaction() };
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) };
EXPECT_EQ(artists.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setRelease(release.get());
}
{
auto transaction{ session.createReadTransaction() };
const auto artists{ Artist::findIds(session, Artist::FindParameters {}.setRelease(release.getId())) };
ASSERT_EQ(artists.results.size(), 1);
EXPECT_EQ(artists.results.front(), artist.getId());
}
}
}
File diff suppressed because it is too large Load Diff
+182 -180
View File
@@ -36,199 +36,201 @@
#include "database/Types.hpp"
#include "database/User.hpp"
TmpDatabase::TmpDatabase()
: _tmpFile{ std::tmpnam(nullptr) }
, _fileDeleter{ _tmpFile }
, _db{ _tmpFile }
namespace lms::db::tests
{
}
Database::Db& TmpDatabase::getDb()
{
return _db;
}
DatabaseFixture::~DatabaseFixture()
{
testDatabaseEmpty();
}
void DatabaseFixture::SetUpTestCase()
{
_tmpDb = std::make_unique<TmpDatabase>();
TmpDatabase::TmpDatabase()
: _tmpFile{ std::tmpnam(nullptr) }
, _fileDeleter{ _tmpFile }
, _db{ _tmpFile }
{
Database::Session s{ _tmpDb->getDb() };
s.prepareTables();
s.analyze();
}
}
void DatabaseFixture::TearDownTestCase()
{
_tmpDb.reset();
}
void DatabaseFixture::testDatabaseEmpty()
{
using namespace Database;
auto transaction{ session.createWriteTransaction() };
EXPECT_EQ(Artist::getCount(session), 0);
EXPECT_EQ(Cluster::getCount(session), 0);
EXPECT_EQ(ClusterType::getCount(session), 0);
EXPECT_EQ(Listen::getCount(session), 0);
EXPECT_EQ(MediaLibrary::getCount(session), 0);
EXPECT_EQ(Release::getCount(session), 0);
EXPECT_EQ(StarredArtist::getCount(session), 0);
EXPECT_EQ(StarredRelease::getCount(session), 0);
EXPECT_EQ(StarredTrack::getCount(session), 0);
EXPECT_EQ(Track::getCount(session), 0);
EXPECT_EQ(TrackBookmark::getCount(session), 0);
EXPECT_EQ(TrackList::getCount(session), 0);
EXPECT_EQ(User::getCount(session), 0);
}
TEST_F(DatabaseFixture, Common_subRangeEmpty)
{
using namespace Database;
RangeResults<int> results;
results.range = Range{ 0, 0 };
results.results = {};
results.moreResults = false;
{
auto subRange{ results.getSubRange(Range {0, 0}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 0);
EXPECT_EQ(subRange.range, Range{});
}
{
auto subRange{ results.getSubRange(Range {0, 1}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 0);
}
}
TEST_F(DatabaseFixture, Common_subRangeForeach)
{
using namespace Database;
struct TestCase
{
Range range;
std::size_t subRangeSize;
std::vector<Range> expectedSubRanges;
};
TestCase testCases[]
{
{Range{0, 0}, 1, {}},
{Range{1, 0}, 1, {}},
{Range{1, 1}, 1, { Range{ 1,1 } }},
{Range{1, 3}, 1, { Range{ 1,1 }, Range {2,1}, Range{3,1} }},
{Range{0, 100}, 100, { Range{0,100} }},
{Range{0, 50}, 100, { Range{0,50} }},
{Range{100, 200}, 100, { Range{100,100}, Range{200,100} }},
{Range{100, 101}, 100, { Range{100,100}, Range{200,1}}},
{Range{1000, 10}, 100, { Range{1000,10} }},
{Range{1, 100}, 50, { Range{1,50}, Range{51, 50} }},
};
for (const TestCase& test : testCases)
{
std::vector<Range> subRanges;
foreachSubRange(test.range, test.subRangeSize, [&](Range subRange)
{
subRanges.push_back(subRange);
return true;
});
EXPECT_EQ(subRanges, test.expectedSubRanges) << ", test index = " << std::distance(std::cbegin(testCases), &test);
}
}
TEST_F(DatabaseFixture, Common_IdType)
{
using namespace Database;
{
const IdType id{};
EXPECT_FALSE(id.isValid());
}
db::Db& TmpDatabase::getDb()
{
const IdType id{ 0 };
EXPECT_TRUE(id.isValid());
return _db;
}
DatabaseFixture::~DatabaseFixture()
{
const IdType id1{ 0 };
const IdType id2{ 0 };
EXPECT_EQ(id1, id2);
testDatabaseEmpty();
}
void DatabaseFixture::SetUpTestCase()
{
const IdType id1{ 0 };
const IdType id2{ 1 };
EXPECT_NE(id1, id2);
EXPECT_LT(id1, id2);
EXPECT_GT(id2, id1);
}
}
TEST_F(DatabaseFixture, Common_subRange)
{
using namespace Database;
RangeResults<int> results;
results.range = Range{ 0, 2 };
results.results = { 5, 6 };
results.moreResults = false;
{
auto subRange{ results.getSubRange(Range {0, 1}) };
EXPECT_TRUE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 1);
EXPECT_EQ(subRange.results.front(), 5);
}
{
auto subRange{ results.getSubRange(Range {1, 1}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 1);
EXPECT_EQ(subRange.results.front(), 6);
}
{
auto subRange{ results.getSubRange(Range {0, 2}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 2);
EXPECT_EQ(subRange.results.front(), 5);
EXPECT_EQ(subRange.results.back(), 6);
}
{
auto subRange{ results.getSubRange(Range {}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 2);
EXPECT_EQ(subRange.results.front(), 5);
EXPECT_EQ(subRange.results.back(), 6);
EXPECT_EQ(subRange.range, results.range);
_tmpDb = std::make_unique<TmpDatabase>();
{
db::Session s{ _tmpDb->getDb() };
s.prepareTables();
s.analyze();
}
}
void DatabaseFixture::TearDownTestCase()
{
auto subRange{ results.getSubRange(Range {1, 0}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 1);
EXPECT_EQ(subRange.results.front(), 6);
const Range expectedRange{ 1, 1 };
EXPECT_EQ(subRange.range, expectedRange);
_tmpDb.reset();
}
{
auto subRange{ results.getSubRange(Range {3, 2}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 0);
const Range expectedRange{ 2, 0 };
EXPECT_EQ(subRange.range, expectedRange);
}
}
void DatabaseFixture::testDatabaseEmpty()
{
using namespace db;
auto transaction{ session.createWriteTransaction() };
EXPECT_EQ(Artist::getCount(session), 0);
EXPECT_EQ(Cluster::getCount(session), 0);
EXPECT_EQ(ClusterType::getCount(session), 0);
EXPECT_EQ(Listen::getCount(session), 0);
EXPECT_EQ(MediaLibrary::getCount(session), 0);
EXPECT_EQ(Release::getCount(session), 0);
EXPECT_EQ(StarredArtist::getCount(session), 0);
EXPECT_EQ(StarredRelease::getCount(session), 0);
EXPECT_EQ(StarredTrack::getCount(session), 0);
EXPECT_EQ(Track::getCount(session), 0);
EXPECT_EQ(TrackBookmark::getCount(session), 0);
EXPECT_EQ(TrackList::getCount(session), 0);
EXPECT_EQ(User::getCount(session), 0);
}
TEST_F(DatabaseFixture, Common_subRangeEmpty)
{
using namespace db;
RangeResults<int> results;
results.range = Range{ 0, 0 };
results.results = {};
results.moreResults = false;
{
auto subRange{ results.getSubRange(Range {0, 0}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 0);
EXPECT_EQ(subRange.range, Range{});
}
{
auto subRange{ results.getSubRange(Range {0, 1}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 0);
}
}
TEST_F(DatabaseFixture, Common_subRangeForeach)
{
using namespace db;
struct TestCase
{
Range range;
std::size_t subRangeSize;
std::vector<Range> expectedSubRanges;
};
TestCase testCases[]
{
{Range{0, 0}, 1, {}},
{Range{1, 0}, 1, {}},
{Range{1, 1}, 1, { Range{ 1,1 } }},
{Range{1, 3}, 1, { Range{ 1,1 }, Range {2,1}, Range{3,1} }},
{Range{0, 100}, 100, { Range{0,100} }},
{Range{0, 50}, 100, { Range{0,50} }},
{Range{100, 200}, 100, { Range{100,100}, Range{200,100} }},
{Range{100, 101}, 100, { Range{100,100}, Range{200,1}}},
{Range{1000, 10}, 100, { Range{1000,10} }},
{Range{1, 100}, 50, { Range{1,50}, Range{51, 50} }},
};
for (const TestCase& test : testCases)
{
std::vector<Range> subRanges;
foreachSubRange(test.range, test.subRangeSize, [&](Range subRange)
{
subRanges.push_back(subRange);
return true;
});
EXPECT_EQ(subRanges, test.expectedSubRanges) << ", test index = " << std::distance(std::cbegin(testCases), &test);
}
}
TEST_F(DatabaseFixture, Common_IdType)
{
using namespace db;
{
const IdType id{};
EXPECT_FALSE(id.isValid());
}
{
const IdType id{ 0 };
EXPECT_TRUE(id.isValid());
}
{
const IdType id1{ 0 };
const IdType id2{ 0 };
EXPECT_EQ(id1, id2);
}
{
const IdType id1{ 0 };
const IdType id2{ 1 };
EXPECT_NE(id1, id2);
EXPECT_LT(id1, id2);
EXPECT_GT(id2, id1);
}
}
TEST_F(DatabaseFixture, Common_subRange)
{
using namespace db;
RangeResults<int> results;
results.range = Range{ 0, 2 };
results.results = { 5, 6 };
results.moreResults = false;
{
auto subRange{ results.getSubRange(Range {0, 1}) };
EXPECT_TRUE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 1);
EXPECT_EQ(subRange.results.front(), 5);
}
{
auto subRange{ results.getSubRange(Range {1, 1}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 1);
EXPECT_EQ(subRange.results.front(), 6);
}
{
auto subRange{ results.getSubRange(Range {0, 2}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 2);
EXPECT_EQ(subRange.results.front(), 5);
EXPECT_EQ(subRange.results.back(), 6);
}
{
auto subRange{ results.getSubRange(Range {}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 2);
EXPECT_EQ(subRange.results.front(), 5);
EXPECT_EQ(subRange.results.back(), 6);
EXPECT_EQ(subRange.range, results.range);
}
{
auto subRange{ results.getSubRange(Range {1, 0}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 1);
EXPECT_EQ(subRange.results.front(), 6);
const Range expectedRange{ 1, 1 };
EXPECT_EQ(subRange.range, expectedRange);
}
{
auto subRange{ results.getSubRange(Range {3, 2}) };
EXPECT_FALSE(subRange.moreResults);
ASSERT_EQ(subRange.results.size(), 0);
const Range expectedRange{ 2, 0 };
EXPECT_EQ(subRange.range, expectedRange);
}
}
}
+101 -99
View File
@@ -40,117 +40,119 @@
#include "database/Types.hpp"
#include "database/User.hpp"
template <typename T>
class [[nodiscard]] ScopedEntity
namespace lms::db::tests
{
public:
using IdType = typename T::IdType;
template <typename... Args>
ScopedEntity(Database::Session& session, Args&& ...args)
: _session{ session }
template <typename T>
class [[nodiscard]] ScopedEntity
{
auto transaction{ _session.createWriteTransaction() };
public:
using IdType = typename T::IdType;
auto entity{ _session.create<T>(std::forward<Args>(args)...) };
EXPECT_TRUE(entity);
_id = entity->getId();
}
template <typename... Args>
ScopedEntity(db::Session& session, Args&& ...args)
: _session{ session }
{
auto transaction{ _session.createWriteTransaction() };
~ScopedEntity()
auto entity{ _session.create<T>(std::forward<Args>(args)...) };
EXPECT_TRUE(entity);
_id = entity->getId();
}
~ScopedEntity()
{
auto transaction{ _session.createWriteTransaction() };
auto entity{ T::find(_session, _id) };
// could not be here due to "on delete cascade" constraints...
if (entity)
entity.remove();
}
ScopedEntity(const ScopedEntity&) = delete;
ScopedEntity(ScopedEntity&&) = delete;
ScopedEntity& operator=(const ScopedEntity&) = delete;
ScopedEntity& operator=(ScopedEntity&&) = delete;
typename T::pointer lockAndGet()
{
auto transaction{ _session.createReadTransaction() };
return get();
}
typename T::pointer get()
{
_session.checkReadTransaction();
auto entity{ T::find(_session, _id) };
EXPECT_TRUE(entity);
return entity;
}
typename T::pointer operator->()
{
return get();
}
IdType getId() const { return _id; }
private:
db::Session& _session;
IdType _id{};
};
using ScopedArtist = ScopedEntity<db::Artist>;
using ScopedCluster = ScopedEntity<db::Cluster>;
using ScopedClusterType = ScopedEntity<db::ClusterType>;
using ScopedMediaLibrary = ScopedEntity<db::MediaLibrary>;
using ScopedRelease = ScopedEntity<db::Release>;
using ScopedTrack = ScopedEntity<db::Track>;
using ScopedTrackList = ScopedEntity<db::TrackList>;
using ScopedUser = ScopedEntity<db::User>;
class ScopedFileDeleter final
{
auto transaction{ _session.createWriteTransaction() };
public:
ScopedFileDeleter(const std::filesystem::path& path) : _path{ path } {}
~ScopedFileDeleter() { std::filesystem::remove(_path); }
auto entity{ T::find(_session, _id) };
// could not be here due to "on delete cascade" constraints...
if (entity)
entity.remove();
}
private:
ScopedFileDeleter(const ScopedFileDeleter&) = delete;
ScopedFileDeleter(ScopedFileDeleter&&) = delete;
ScopedFileDeleter operator=(const ScopedFileDeleter&) = delete;
ScopedFileDeleter operator=(ScopedFileDeleter&&) = delete;
ScopedEntity(const ScopedEntity&) = delete;
ScopedEntity(ScopedEntity&&) = delete;
ScopedEntity& operator=(const ScopedEntity&) = delete;
ScopedEntity& operator=(ScopedEntity&&) = delete;
const std::filesystem::path _path;
};
typename T::pointer lockAndGet()
class TmpDatabase final
{
auto transaction{ _session.createReadTransaction() };
return get();
}
public:
TmpDatabase();
typename T::pointer get()
db::Db& getDb();
private:
const std::filesystem::path _tmpFile;
ScopedFileDeleter _fileDeleter;
db::Db _db;
};
class DatabaseFixture : public ::testing::Test
{
_session.checkReadTransaction();
public:
~DatabaseFixture();
auto entity{ T::find(_session, _id) };
EXPECT_TRUE(entity);
return entity;
}
public:
static void SetUpTestCase();
static void TearDownTestCase();
typename T::pointer operator->()
{
return get();
}
private:
void testDatabaseEmpty();
IdType getId() const { return _id; }
private:
Database::Session& _session;
IdType _id{};
};
using ScopedArtist = ScopedEntity<Database::Artist>;
using ScopedCluster = ScopedEntity<Database::Cluster>;
using ScopedClusterType = ScopedEntity<Database::ClusterType>;
using ScopedMediaLibrary = ScopedEntity<Database::MediaLibrary>;
using ScopedRelease = ScopedEntity<Database::Release>;
using ScopedTrack = ScopedEntity<Database::Track>;
using ScopedTrackList = ScopedEntity<Database::TrackList>;
using ScopedUser = ScopedEntity<Database::User>;
class ScopedFileDeleter final
{
public:
ScopedFileDeleter(const std::filesystem::path& path) : _path{ path } {}
~ScopedFileDeleter() { std::filesystem::remove(_path); }
private:
ScopedFileDeleter(const ScopedFileDeleter&) = delete;
ScopedFileDeleter(ScopedFileDeleter&&) = delete;
ScopedFileDeleter operator=(const ScopedFileDeleter&) = delete;
ScopedFileDeleter operator=(ScopedFileDeleter&&) = delete;
const std::filesystem::path _path;
};
class TmpDatabase final
{
public:
TmpDatabase();
Database::Db& getDb();
private:
const std::filesystem::path _tmpFile;
ScopedFileDeleter _fileDeleter;
Database::Db _db;
};
class DatabaseFixture : public ::testing::Test
{
public:
~DatabaseFixture();
public:
static void SetUpTestCase();
static void TearDownTestCase();
private:
void testDatabaseEmpty();
static inline std::unique_ptr<TmpDatabase> _tmpDb{};
public:
Database::Session session{ _tmpDb->getDb() };
};
static inline std::unique_ptr<TmpDatabase> _tmpDb{};
public:
db::Session session{ _tmpDb->getDb() };
};
}
+67 -66
View File
@@ -21,84 +21,85 @@
#include "Common.hpp"
using namespace Database;
TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease)
namespace lms::db::tests
{
constexpr std::size_t nbTracks{ 10 };
std::list<ScopedTrack> tracks;
ScopedArtist artist{ session, "MyArtst" };
ScopedRelease release{ session, "MyRelease" };
for (std::size_t i{}; i < nbTracks; ++i)
TEST_F(DatabaseFixture, MultiTracksSingleArtistSingleRelease)
{
tracks.emplace_back(session, "MyTrack" + std::to_string(i));
constexpr std::size_t nbTracks{ 10 };
std::list<ScopedTrack> tracks;
ScopedArtist artist{ session, "MyArtst" };
ScopedRelease release{ session, "MyRelease" };
auto transaction{ session.createWriteTransaction() };
for (std::size_t i{}; i < nbTracks; ++i)
{
tracks.emplace_back(session, "MyTrack" + std::to_string(i));
TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist);
tracks.back().get().modify()->setRelease(release.get());
auto transaction{ session.createWriteTransaction() };
TrackArtistLink::create(session, tracks.back().get(), artist.get(), TrackArtistLinkType::Artist);
tracks.back().get().modify()->setRelease(release.get());
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(Release::findOrphanIds(session).results.empty());
EXPECT_TRUE(Artist::findOrphanIds(session).results.empty());
}
{
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());
const auto releaseTracks{ Track::find(session, Track::FindParameters {}.setRelease(release.getId())) };
EXPECT_EQ(releaseTracks.results.size(), nbTracks);
}
}
TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist)
{
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(Release::findOrphanIds(session).results.empty());
EXPECT_TRUE(Artist::findOrphanIds(session).results.empty());
ScopedTrack track{ session, "MyTrack" };
ScopedRelease release{ session, "MyRelease" };
ScopedArtist artist{ session, "MyArtist" };
{
auto transaction{ session.createWriteTransaction() };
auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) };
track.get().modify()->setRelease(release.get());
}
{
auto transaction{ session.createWriteTransaction() };
auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) };
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
auto artists{ release->getArtists() };
ASSERT_EQ(artists.size(), 1);
ASSERT_EQ(artists.front()->getId(), artist.getId());
}
}
TEST_F(DatabaseFixture, SingleUser)
{
auto transaction{ session.createReadTransaction() };
{
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(User::find(session, User::FindParameters{}).results.empty());
EXPECT_EQ(User::getCount(session), 0);
}
auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) };
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
ScopedUser user{ session, "MyUser" };
const auto releaseTracks{ Track::find(session, Track::FindParameters {}.setRelease(release.getId())) };
EXPECT_EQ(releaseTracks.results.size(), nbTracks);
}
}
{
auto transaction{ session.createReadTransaction() };
TEST_F(DatabaseFixture, SingleTrackSingleReleaseSingleArtist)
{
ScopedTrack track{ session, "MyTrack" };
ScopedRelease release{ session, "MyRelease" };
ScopedArtist artist{ session, "MyArtist" };
{
auto transaction{ session.createWriteTransaction() };
auto trackArtistLink{ TrackArtistLink::create(session, track.get(), artist.get(), TrackArtistLinkType::Artist) };
track.get().modify()->setRelease(release.get());
}
{
auto transaction{ session.createWriteTransaction() };
auto releases{ Release::findIds(session, Release::FindParameters {}.setArtist(artist.getId())) };
ASSERT_EQ(releases.results.size(), 1);
EXPECT_EQ(releases.results.front(), release.getId());
auto artists{ release->getArtists() };
ASSERT_EQ(artists.size(), 1);
ASSERT_EQ(artists.front()->getId(), artist.getId());
}
}
TEST_F(DatabaseFixture, SingleUser)
{
{
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(User::find(session, User::FindParameters{}).results.empty());
EXPECT_EQ(User::getCount(session), 0);
}
ScopedUser user{ session, "MyUser" };
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(User::find(session, User::FindParameters{}).results.size(), 1);
EXPECT_EQ(User::getCount(session), 1);
EXPECT_EQ(User::find(session, User::FindParameters{}).results.size(), 1);
EXPECT_EQ(User::getCount(session), 1);
}
}
}
@@ -106,4 +107,4 @@ int main(int argc, char** argv)
{
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+111 -110
View File
@@ -20,129 +20,130 @@
#include "Common.hpp"
#include "database/StarredArtist.hpp"
using namespace Database;
using ScopedStarredArtist = ScopedEntity<Database::StarredArtist>;
TEST_F(DatabaseFixture, StarredArtist)
namespace lms::db::tests
{
ScopedArtist artist{ session, "MyArtist" };
ScopedUser user{ session, "MyUser" };
ScopedUser user2{ session, "MyUser2" };
using ScopedStarredArtist = ScopedEntity<db::StarredArtist>;
TEST_F(DatabaseFixture, StarredArtist)
{
auto transaction{ session.createReadTransaction() };
ScopedArtist artist{ session, "MyArtist" };
ScopedUser user{ session, "MyUser" };
ScopedUser user2{ session, "MyUser2" };
auto starredArtist{ StarredArtist::find(session, artist->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_FALSE(starredArtist);
EXPECT_EQ(StarredArtist::getCount(session), 0);
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters {}) };
EXPECT_EQ(artists.results.size(), 1);
auto starredArtist{ StarredArtist::find(session, artist->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_FALSE(starredArtist);
EXPECT_EQ(StarredArtist::getCount(session), 0);
auto artists{ Artist::findIds(session, Artist::FindParameters {}) };
EXPECT_EQ(artists.results.size(), 1);
}
ScopedStarredArtist starredArtist{ session, artist.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
{
auto transaction{ session.createReadTransaction() };
auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_EQ(gotArtist->getId(), starredArtist->getId());
EXPECT_EQ(StarredArtist::getCount(session), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters {}) };
EXPECT_EQ(artists.results.size(), 1);
artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(artists.results.size(), 1);
artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal));
EXPECT_EQ(artists.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz);
}
{
auto transaction{ session.createReadTransaction() };
auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) };
EXPECT_EQ(gotArtist, Artist::pointer{});
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::Internal);
}
{
auto transaction{ session.createWriteTransaction() };
auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) };
EXPECT_EQ(gotArtist->getId(), starredArtist->getId());
}
}
ScopedStarredArtist starredArtist{ session, artist.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
TEST_F(DatabaseFixture, StarredArtist_PendingDestroy)
{
auto transaction{ session.createReadTransaction() };
ScopedArtist artist{ session, "MyArtist" };
ScopedUser user{ session, "MyUser" };
ScopedStarredArtist starredArtist{ session, artist.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_EQ(gotArtist->getId(), starredArtist->getId());
EXPECT_EQ(StarredArtist::getCount(session), 1);
{
auto transaction{ session.createWriteTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(artists.results.size(), 1);
starredArtist.get().modify()->setSyncState(SyncState::PendingRemove);
artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(artists.results.size(), 0);
}
}
TEST_F(DatabaseFixture, StarredArtist_dateTime)
{
auto transaction{ session.createReadTransaction() };
ScopedArtist artist1{ session, "MyArtist1" };
ScopedArtist artist2{ session, "MyArtist2" };
ScopedUser user{ session, "MyUser" };
auto artists{ Artist::findIds(session, Artist::FindParameters {}) };
EXPECT_EQ(artists.results.size(), 1);
ScopedStarredArtist starredArtist1{ session, artist1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
ScopedStarredArtist starredArtist2{ session, artist2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(artists.results.size(), 1);
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} };
artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal));
EXPECT_EQ(artists.results.size(), 0);
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(artists.results.size(), 2);
}
{
auto transaction{ session.createWriteTransaction() };
starredArtist1.get().modify()->setDateTime(dateTime);
starredArtist2.get().modify()->setDateTime(dateTime.addSecs(-1));
auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], starredArtist1->getArtist()->getId());
EXPECT_EQ(artists.results[1], starredArtist2->getArtist()->getId());
}
{
auto transaction{ session.createWriteTransaction() };
starredArtist1.get().modify()->setDateTime(dateTime);
starredArtist2.get().modify()->setDateTime(dateTime.addSecs(1));
auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], starredArtist2->getArtist()->getId());
EXPECT_EQ(artists.results[1], starredArtist1->getArtist()->getId());
}
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz);
}
{
auto transaction{ session.createReadTransaction() };
auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) };
EXPECT_EQ(gotArtist, Artist::pointer{});
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::Internal);
}
{
auto transaction{ session.createWriteTransaction() };
auto gotArtist{ StarredArtist::find(session, artist->getId(), user->getId()) };
EXPECT_EQ(gotArtist->getId(), starredArtist->getId());
}
}
TEST_F(DatabaseFixture, StarredArtist_PendingDestroy)
{
ScopedArtist artist{ session, "MyArtist" };
ScopedUser user{ session, "MyUser" };
ScopedStarredArtist starredArtist{ session, artist.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
{
auto transaction{ session.createWriteTransaction() };
auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(artists.results.size(), 1);
starredArtist.get().modify()->setSyncState(SyncState::PendingRemove);
artists = Artist::findIds(session, Artist::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(artists.results.size(), 0);
}
}
TEST_F(DatabaseFixture, StarredArtist_dateTime)
{
ScopedArtist artist1{ session, "MyArtist1" };
ScopedArtist artist2{ session, "MyArtist2" };
ScopedUser user{ session, "MyUser" };
ScopedStarredArtist starredArtist1{ session, artist1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
ScopedStarredArtist starredArtist2{ session, artist2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} };
{
auto transaction{ session.createReadTransaction() };
auto artists{ Artist::find(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(artists.results.size(), 2);
}
{
auto transaction{ session.createWriteTransaction() };
starredArtist1.get().modify()->setDateTime(dateTime);
starredArtist2.get().modify()->setDateTime(dateTime.addSecs(-1));
auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], starredArtist1->getArtist()->getId());
EXPECT_EQ(artists.results[1], starredArtist2->getArtist()->getId());
}
{
auto transaction{ session.createWriteTransaction() };
starredArtist1.get().modify()->setDateTime(dateTime);
starredArtist2.get().modify()->setDateTime(dateTime.addSecs(1));
auto artists{ Artist::findIds(session, Artist::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ArtistSortMethod::StarredDateDesc)) };
ASSERT_EQ(artists.results.size(), 2);
EXPECT_EQ(artists.results[0], starredArtist2->getArtist()->getId());
EXPECT_EQ(artists.results[1], starredArtist1->getArtist()->getId());
}
}
}
+100 -99
View File
@@ -20,118 +20,119 @@
#include "Common.hpp"
#include "database/StarredRelease.hpp"
using namespace Database;
using ScopedStarredRelease = ScopedEntity<Database::StarredRelease>;
TEST_F(DatabaseFixture, StarredRelease)
namespace lms::db::tests
{
ScopedRelease release{ session, "MyRelease" };
ScopedUser user{ session, "MyUser" };
ScopedUser user2{ session, "MyUser2" };
using ScopedStarredRelease = ScopedEntity<db::StarredRelease>;
TEST_F(DatabaseFixture, StarredRelease)
{
auto transaction{ session.createReadTransaction() };
ScopedRelease release{ session, "MyRelease" };
ScopedUser user{ session, "MyUser" };
ScopedUser user2{ session, "MyUser2" };
auto starredRelease{ StarredRelease::find(session, release->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_FALSE(starredRelease);
EXPECT_EQ(StarredRelease::getCount(session), 0);
{
auto transaction{ session.createReadTransaction() };
auto releases{ Release::find(session, Release::FindParameters {}) };
EXPECT_EQ(releases.results.size(), 1);
auto starredRelease{ StarredRelease::find(session, release->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_FALSE(starredRelease);
EXPECT_EQ(StarredRelease::getCount(session), 0);
auto releases{ Release::find(session, Release::FindParameters {}) };
EXPECT_EQ(releases.results.size(), 1);
}
ScopedStarredRelease starredRelease{ session, release.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
{
auto transaction{ session.createReadTransaction() };
auto gotRelease{ StarredRelease::find(session, release->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_EQ(gotRelease->getId(), starredRelease->getId());
EXPECT_EQ(StarredRelease::getCount(session), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto releases{ Release::find(session, Release::FindParameters {}) };
EXPECT_EQ(releases.results.size(), 1);
releases = Release::find(session, Release::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(releases.results.size(), 1);
releases = Release::find(session, Release::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal));
EXPECT_EQ(releases.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz);
}
{
auto transaction{ session.createReadTransaction() };
auto gotRelease{ StarredRelease::find(session, release->getId(), user->getId()) };
EXPECT_EQ(gotRelease, StarredRelease::pointer{});
}
}
ScopedStarredRelease starredRelease{ session, release.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
TEST_F(DatabaseFixture, Starredrelease_PendingDestroy)
{
auto transaction{ session.createReadTransaction() };
ScopedRelease release{ session, "MyRelease" };
ScopedUser user{ session, "MyUser" };
ScopedStarredRelease starredRelease{ session, release.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
auto gotRelease{ StarredRelease::find(session, release->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_EQ(gotRelease->getId(), starredRelease->getId());
EXPECT_EQ(StarredRelease::getCount(session), 1);
{
auto transaction{ session.createWriteTransaction() };
auto releases{ Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(releases.results.size(), 1);
starredRelease.get().modify()->setSyncState(SyncState::PendingRemove);
releases = Release::find(session, Release::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(releases.results.size(), 0);
}
}
TEST_F(DatabaseFixture, StarredRelease_dateTime)
{
auto transaction{ session.createReadTransaction() };
ScopedRelease release1{ session, "MyRelease1" };
ScopedRelease release2{ session, "MyRelease2" };
ScopedUser user{ session, "MyUser" };
auto releases{ Release::find(session, Release::FindParameters {}) };
EXPECT_EQ(releases.results.size(), 1);
ScopedStarredRelease starredRelease1{ session, release1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
ScopedStarredRelease starredRelease2{ session, release2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
releases = Release::find(session, Release::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(releases.results.size(), 1);
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} };
releases = Release::find(session, Release::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal));
EXPECT_EQ(releases.results.size(), 0);
{
auto transaction{ session.createReadTransaction() };
auto releases{ Release::findIds(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(releases.results.size(), 2);
}
{
auto transaction{ session.createWriteTransaction() };
starredRelease1.get().modify()->setDateTime(dateTime);
starredRelease2.get().modify()->setDateTime(dateTime.addSecs(-1));
auto releases{ Release::findIds(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ReleaseSortMethod::StarredDateDesc)) };
ASSERT_EQ(releases.results.size(), 2);
EXPECT_EQ(releases.results[0], starredRelease1->getRelease()->getId());
EXPECT_EQ(releases.results[1], starredRelease2->getRelease()->getId());
}
{
auto transaction{ session.createWriteTransaction() };
starredRelease1.get().modify()->setDateTime(dateTime);
starredRelease2.get().modify()->setDateTime(dateTime.addSecs(1));
auto releases{ Release::findIds(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ReleaseSortMethod::StarredDateDesc)) };
ASSERT_EQ(releases.results.size(), 2);
EXPECT_EQ(releases.results[0], starredRelease2->getRelease()->getId());
EXPECT_EQ(releases.results[1], starredRelease1->getRelease()->getId());
}
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz);
}
{
auto transaction{ session.createReadTransaction() };
auto gotRelease{ StarredRelease::find(session, release->getId(), user->getId()) };
EXPECT_EQ(gotRelease, StarredRelease::pointer{});
}
}
TEST_F(DatabaseFixture, Starredrelease_PendingDestroy)
{
ScopedRelease release{ session, "MyRelease" };
ScopedUser user{ session, "MyUser" };
ScopedStarredRelease starredRelease{ session, release.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
{
auto transaction{ session.createWriteTransaction() };
auto releases{ Release::find(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(releases.results.size(), 1);
starredRelease.get().modify()->setSyncState(SyncState::PendingRemove);
releases = Release::find(session, Release::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(releases.results.size(), 0);
}
}
TEST_F(DatabaseFixture, StarredRelease_dateTime)
{
ScopedRelease release1{ session, "MyRelease1" };
ScopedRelease release2{ session, "MyRelease2" };
ScopedUser user{ session, "MyUser" };
ScopedStarredRelease starredRelease1{ session, release1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
ScopedStarredRelease starredRelease2{ session, release2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} };
{
auto transaction{ session.createReadTransaction() };
auto releases{ Release::findIds(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(releases.results.size(), 2);
}
{
auto transaction{ session.createWriteTransaction() };
starredRelease1.get().modify()->setDateTime(dateTime);
starredRelease2.get().modify()->setDateTime(dateTime.addSecs(-1));
auto releases{ Release::findIds(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ReleaseSortMethod::StarredDateDesc)) };
ASSERT_EQ(releases.results.size(), 2);
EXPECT_EQ(releases.results[0], starredRelease1->getRelease()->getId());
EXPECT_EQ(releases.results[1], starredRelease2->getRelease()->getId());
}
{
auto transaction{ session.createWriteTransaction() };
starredRelease1.get().modify()->setDateTime(dateTime);
starredRelease2.get().modify()->setDateTime(dateTime.addSecs(1));
auto releases{ Release::findIds(session, Release::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(ReleaseSortMethod::StarredDateDesc)) };
ASSERT_EQ(releases.results.size(), 2);
EXPECT_EQ(releases.results[0], starredRelease2->getRelease()->getId());
EXPECT_EQ(releases.results[1], starredRelease1->getRelease()->getId());
}
}
}
+100 -99
View File
@@ -20,118 +20,119 @@
#include "Common.hpp"
#include "database/StarredTrack.hpp"
using namespace Database;
using ScopedStarredTrack = ScopedEntity<Database::StarredTrack>;
TEST_F(DatabaseFixture, StarredTrack)
namespace lms::db::tests
{
ScopedTrack track{ session, "MyTrack" };
ScopedUser user{ session, "MyUser" };
ScopedUser user2{ session, "MyUser2" };
using ScopedStarredTrack = ScopedEntity<db::StarredTrack>;
TEST_F(DatabaseFixture, StarredTrack)
{
auto transaction {session.createReadTransaction()};
ScopedTrack track{ session, "MyTrack" };
ScopedUser user{ session, "MyUser" };
ScopedUser user2{ session, "MyUser2" };
auto starredTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_FALSE(starredTrack);
EXPECT_EQ(StarredTrack::getCount(session), 0);
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}) };
EXPECT_EQ(tracks.results.size(), 1);
auto starredTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_FALSE(starredTrack);
EXPECT_EQ(StarredTrack::getCount(session), 0);
auto tracks{ Track::findIds(session, Track::FindParameters {}) };
EXPECT_EQ(tracks.results.size(), 1);
}
ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
{
auto transaction{ session.createReadTransaction() };
auto gotTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_EQ(gotTrack->getId(), starredTrack->getId());
EXPECT_EQ(StarredTrack::getCount(session), 1);
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}) };
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(tracks.results.size(), 1);
tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal));
EXPECT_EQ(tracks.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz);
}
{
auto transaction{ session.createReadTransaction() };
auto gotRelease{ StarredTrack::find(session, track->getId(), user->getId()) };
EXPECT_EQ(gotRelease, StarredTrack::pointer{});
}
}
ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
TEST_F(DatabaseFixture, Starredtrack_PendingDestroy)
{
auto transaction {session.createReadTransaction()};
ScopedTrack track{ session, "MyTrack" };
ScopedUser user{ session, "MyUser" };
ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
auto gotTrack{ StarredTrack::find(session, track->getId(), user->getId(), FeedbackBackend::Internal) };
EXPECT_EQ(gotTrack->getId(), starredTrack->getId());
EXPECT_EQ(StarredTrack::getCount(session), 1);
{
auto transaction{ session.createWriteTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(tracks.results.size(), 1);
starredTrack.get().modify()->setSyncState(SyncState::PendingRemove);
tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(tracks.results.size(), 0);
}
}
TEST_F(DatabaseFixture, StarredTrack_dateTime)
{
auto transaction {session.createReadTransaction()};
ScopedTrack track1{ session, "MyTrack1" };
ScopedTrack track2{ session, "MyTrack2" };
ScopedUser user{ session, "MyUser" };
auto tracks{ Track::findIds(session, Track::FindParameters {}) };
EXPECT_EQ(tracks.results.size(), 1);
ScopedStarredTrack starredTrack1{ session, track1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
ScopedStarredTrack starredTrack2{ session, track2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(tracks.results.size(), 1);
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} };
tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user2.getId(), FeedbackBackend::Internal));
EXPECT_EQ(tracks.results.size(), 0);
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(tracks.results.size(), 2);
}
{
auto transaction{ session.createWriteTransaction() };
starredTrack1.get().modify()->setDateTime(dateTime);
starredTrack2.get().modify()->setDateTime(dateTime.addSecs(-1));
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc)) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], starredTrack1->getTrack()->getId());
EXPECT_EQ(tracks.results[1], starredTrack2->getTrack()->getId());
}
{
auto transaction{ session.createWriteTransaction() };
starredTrack1.get().modify()->setDateTime(dateTime);
starredTrack2.get().modify()->setDateTime(dateTime.addSecs(1));
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc)) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], starredTrack2->getTrack()->getId());
EXPECT_EQ(tracks.results[1], starredTrack1->getTrack()->getId());
}
}
{
auto transaction{ session.createWriteTransaction() };
user.get().modify()->setFeedbackBackend(FeedbackBackend::ListenBrainz);
}
{
auto transaction{ session.createReadTransaction() };
auto gotRelease{ StarredTrack::find(session, track->getId(), user->getId()) };
EXPECT_EQ(gotRelease, StarredTrack::pointer{});
}
}
TEST_F(DatabaseFixture, Starredtrack_PendingDestroy)
{
ScopedTrack track{ session, "MyTrack" };
ScopedUser user{ session, "MyUser" };
ScopedStarredTrack starredTrack{ session, track.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
{
auto transaction {session.createWriteTransaction()};
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(tracks.results.size(), 1);
starredTrack.get().modify()->setSyncState(SyncState::PendingRemove);
tracks = Track::findIds(session, Track::FindParameters{}.setStarringUser(user.getId(), FeedbackBackend::Internal));
EXPECT_EQ(tracks.results.size(), 0);
}
}
TEST_F(DatabaseFixture, StarredTrack_dateTime)
{
ScopedTrack track1{ session, "MyTrack1" };
ScopedTrack track2{ session, "MyTrack2" };
ScopedUser user{ session, "MyUser" };
ScopedStarredTrack starredTrack1{ session, track1.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
ScopedStarredTrack starredTrack2{ session, track2.lockAndGet(), user.lockAndGet(), FeedbackBackend::Internal };
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 2}, Wt::WTime {12, 30, 1} };
{
auto transaction {session.createReadTransaction()};
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal)) };
EXPECT_EQ(tracks.results.size(), 2);
}
{
auto transaction {session.createWriteTransaction()};
starredTrack1.get().modify()->setDateTime(dateTime);
starredTrack2.get().modify()->setDateTime(dateTime.addSecs(-1));
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc)) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], starredTrack1->getTrack()->getId());
EXPECT_EQ(tracks.results[1], starredTrack2->getTrack()->getId());
}
{
auto transaction {session.createWriteTransaction()};
starredTrack1.get().modify()->setDateTime(dateTime);
starredTrack2.get().modify()->setDateTime(dateTime.addSecs(1));
auto tracks{ Track::findIds(session, Track::FindParameters {}.setStarringUser(user.getId(), FeedbackBackend::Internal).setSortMethod(TrackSortMethod::StarredDateDesc)) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], starredTrack2->getTrack()->getId());
EXPECT_EQ(tracks.results[1], starredTrack1->getTrack()->getId());
}
}
}
+193 -193
View File
@@ -21,224 +21,224 @@
#include <algorithm>
using namespace Database;
TEST_F(DatabaseFixture, Track)
namespace lms::db::tests
{
TEST_F(DatabaseFixture, Track)
{
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Track::find(session, Track::FindParameters{}).results.size(), 0);
EXPECT_EQ(Track::findIds(session, Track::FindParameters{}).results.size(), 0);
EXPECT_EQ(Track::getCount(session), 0);
EXPECT_FALSE(Track::exists(session, 0));
{
bool visited{};
Track::find(session, Track::FindParameters{}, [&](const Track::pointer&) {visited = true;});
EXPECT_FALSE(visited);
}
}
ScopedTrack track{ session, "MyTrackFile" };
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Track::find(session, Track::FindParameters{}).results.size(), 1);
EXPECT_EQ(Track::getCount(session), 1);
EXPECT_TRUE(Track::exists(session, track.getId()));
auto myTrack{ Track::find(session, track.getId()) };
ASSERT_TRUE(myTrack);
EXPECT_EQ(myTrack->getId(), track.getId());
{
bool visited{};
Track::find(session, Track::FindParameters{}, [&](const Track::pointer& t)
{
visited = true;
EXPECT_EQ(t->getId(), track.getId());
});
EXPECT_TRUE(visited);
}
}
}
TEST_F(DatabaseFixture, Track_MediaLibrary)
{
ScopedTrack track{ session, "MyTrackFile" };
ScopedMediaLibrary library{ session };
ScopedMediaLibrary otherLibrary{ session };
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setMediaLibrary(library.get());
}
{
auto transaction{ session.createWriteTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters{}.setMediaLibrary(library->getId())) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track.getId());
}
{
auto transaction{ session.createWriteTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters{}.setMediaLibrary(otherLibrary->getId())) };
EXPECT_EQ(tracks.results.size(), 0);
}
}
TEST_F(DatabaseFixture, Track_noMediaLibrary)
{
ScopedTrack track{ session, "MyTrackFile" };
{
auto transaction{ session.createReadTransaction() };
MediaLibrary::pointer mediaLibrary{ track->getMediaLibrary() };
EXPECT_EQ(mediaLibrary, MediaLibrary::pointer{});
EXPECT_FALSE(mediaLibrary);
EXPECT_TRUE(!mediaLibrary);
}
}
TEST_F(DatabaseFixture, TrackNotExists)
{
auto transaction{ session.createReadTransaction() };
EXPECT_FALSE(Track::exists(session, TrackId{ 42 }));
EXPECT_EQ(Track::find(session, TrackId{ 42 }), Track::pointer{});
EXPECT_FALSE(Track::find(session, TrackId{ 42 }));
EXPECT_EQ(Track::find(session, Track::FindParameters{}).results.size(), 0);
EXPECT_EQ(Track::findIds(session, Track::FindParameters{}).results.size(), 0);
EXPECT_EQ(Track::getCount(session), 0);
EXPECT_FALSE(Track::exists(session, 0));
{
bool visited{};
Track::find(session, Track::FindParameters{}, [&](const Track::pointer&) {visited = true;});
EXPECT_FALSE(visited);
auto track{ Track::find(session, TrackId{ 42 }) };
EXPECT_TRUE(!track);
EXPECT_FALSE(track);
}
}
ScopedTrack track{ session, "MyTrackFile" };
TEST_F(DatabaseFixture, MultipleTracks)
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(Track::find(session, Track::FindParameters{}).results.size(), 1);
EXPECT_EQ(Track::getCount(session), 1);
EXPECT_TRUE(Track::exists(session, track.getId()));
auto myTrack{ Track::find(session, track.getId()) };
ASSERT_TRUE(myTrack);
EXPECT_EQ(myTrack->getId(), track.getId());
ScopedTrack track1{ session, "MyTrackFile1" };
ScopedTrack track2{ session, "MyTrackFile2" };
{
bool visited{};
Track::find(session, Track::FindParameters{}, [&](const Track::pointer& t)
{
visited = true;
EXPECT_EQ(t->getId(), track.getId());
});
EXPECT_TRUE(visited);
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(track1.getId() != track2.getId());
EXPECT_TRUE(track1.get() != track2.get());
EXPECT_FALSE(track1.get() == track2.get());
}
}
}
TEST_F(DatabaseFixture, Track_MediaLibrary)
{
ScopedTrack track{ session, "MyTrackFile" };
ScopedMediaLibrary library{ session };
ScopedMediaLibrary otherLibrary{ session };
TEST_F(DatabaseFixture, MultipleTracksSearchByFilter)
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setMediaLibrary(library.get());
}
{
auto transaction{ session.createWriteTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters{}.setMediaLibrary(library->getId()))};
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track.getId());
}
{
auto transaction{ session.createWriteTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters{}.setMediaLibrary(otherLibrary->getId()))};
EXPECT_EQ(tracks.results.size(), 0);
}
}
TEST_F(DatabaseFixture, Track_noMediaLibrary)
{
ScopedTrack track{ session, "MyTrackFile" };
{
auto transaction{ session.createReadTransaction() };
MediaLibrary::pointer mediaLibrary{ track->getMediaLibrary() };
EXPECT_EQ(mediaLibrary, MediaLibrary::pointer{});
EXPECT_FALSE(mediaLibrary);
EXPECT_TRUE(!mediaLibrary);
}
}
TEST_F(DatabaseFixture, TrackNotExists)
{
auto transaction{ session.createReadTransaction() };
EXPECT_FALSE(Track::exists(session, TrackId{ 42 }));
EXPECT_EQ(Track::find(session, TrackId{ 42 }), Track::pointer{});
EXPECT_FALSE(Track::find(session, TrackId{ 42 }));
EXPECT_EQ(Track::find(session, Track::FindParameters{}).results.size(), 0);
{
auto track{ Track::find(session, TrackId{ 42 }) };
EXPECT_TRUE(!track);
EXPECT_FALSE(track);
}
}
TEST_F(DatabaseFixture, MultipleTracks)
{
ScopedTrack track1{ session, "MyTrackFile1" };
ScopedTrack track2{ session, "MyTrackFile2" };
{
auto transaction{ session.createReadTransaction() };
EXPECT_TRUE(track1.getId() != track2.getId());
EXPECT_TRUE(track1.get() != track2.get());
EXPECT_FALSE(track1.get() == track2.get());
}
}
TEST_F(DatabaseFixture, MultipleTracksSearchByFilter)
{
ScopedTrack track1{ session, "" };
ScopedTrack track2{ session, "" };
ScopedTrack track3{ session, "" };
ScopedTrack track4{ session, "" };
ScopedTrack track5{ session, "" };
ScopedTrack track6{ session, "" };
{
auto transaction{ session.createWriteTransaction() };
track1.get().modify()->setName("MyTrack");
track2.get().modify()->setName("MyTrack%");
track3.get().modify()->setName("MyTrack%Foo");
track4.get().modify()->setName("%MyTrack");
track5.get().modify()->setName("Foo%MyTrack");
track6.get().modify()->setName("M_Track");
}
{
auto transaction{ session.createReadTransaction() };
ScopedTrack track1{ session, "" };
ScopedTrack track2{ session, "" };
ScopedTrack track3{ session, "" };
ScopedTrack track4{ session, "" };
ScopedTrack track5{ session, "" };
ScopedTrack track6{ session, "" };
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"Track"})) };
EXPECT_EQ(tracks.results.size(), 6);
auto transaction{ session.createWriteTransaction() };
track1.get().modify()->setName("MyTrack");
track2.get().modify()->setName("MyTrack%");
track3.get().modify()->setName("MyTrack%Foo");
track4.get().modify()->setName("%MyTrack");
track5.get().modify()->setName("Foo%MyTrack");
track6.get().modify()->setName("M_Track");
}
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"MyTrack"})) };
EXPECT_EQ(tracks.results.size(), 5);
EXPECT_TRUE(std::none_of(std::cbegin(tracks.results), std::cend(tracks.results), [&](const TrackId trackId) { return trackId == track6.getId(); }));
auto transaction{ session.createReadTransaction() };
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"Track"})) };
EXPECT_EQ(tracks.results.size(), 6);
}
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"MyTrack"})) };
EXPECT_EQ(tracks.results.size(), 5);
EXPECT_TRUE(std::none_of(std::cbegin(tracks.results), std::cend(tracks.results), [&](const TrackId trackId) { return trackId == track6.getId(); }));
}
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"MyTrack%"})) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], track2.getId());
EXPECT_EQ(tracks.results[1], track3.getId());
}
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"%MyTrack"})) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], track4.getId());
EXPECT_EQ(tracks.results[1], track5.getId());
}
}
}
TEST_F(DatabaseFixture, Track_date)
{
ScopedTrack track{ session, "MyTrack" };
const Wt::WDate date{ 1995, 5, 5 };
const Wt::WDate originalDate{ 1994, 2, 2 };
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"MyTrack%"})) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], track2.getId());
EXPECT_EQ(tracks.results[1], track3.getId());
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(track->getYear(), std::nullopt);
EXPECT_EQ(track->getOriginalYear(), std::nullopt);
}
{
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setKeywords({"%MyTrack"})) };
ASSERT_EQ(tracks.results.size(), 2);
EXPECT_EQ(tracks.results[0], track4.getId());
EXPECT_EQ(tracks.results[1], track5.getId());
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setDate(date);
track.get().modify()->setOriginalDate(originalDate);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(track->getYear(), std::nullopt);
EXPECT_EQ(track->getOriginalYear(), std::nullopt);
EXPECT_EQ(track->getDate(), date);
EXPECT_EQ(track->getOriginalDate(), originalDate);
}
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setYear(date.year());
track.get().modify()->setOriginalYear(originalDate.year());
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(track->getYear(), date.year());
EXPECT_EQ(track->getOriginalYear(), originalDate.year());
}
}
}
TEST_F(DatabaseFixture, Track_date)
{
ScopedTrack track{ session, "MyTrack" };
const Wt::WDate date{ 1995, 5, 5 };
const Wt::WDate originalDate{ 1994, 2, 2 };
TEST_F(DatabaseFixture, Track_writtenAfter)
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(track->getYear(), std::nullopt);
EXPECT_EQ(track->getOriginalYear(), std::nullopt);
ScopedTrack track{ session, "MyTrack" };
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 1}, Wt::WTime {12, 30, 20} };
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setLastWriteTime(dateTime);
}
{
auto transaction{ session.createReadTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters {}) };
EXPECT_EQ(tracks.results.size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setWrittenAfter(dateTime.addSecs(-1))) };
EXPECT_EQ(tracks.results.size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setWrittenAfter(dateTime.addSecs(+1))) };
EXPECT_EQ(tracks.results.size(), 0);
}
}
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setDate(date);
track.get().modify()->setOriginalDate(originalDate);
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(track->getYear(), std::nullopt);
EXPECT_EQ(track->getOriginalYear(), std::nullopt);
EXPECT_EQ(track->getDate(), date);
EXPECT_EQ(track->getOriginalDate(), originalDate);
}
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setYear(date.year());
track.get().modify()->setOriginalYear(originalDate.year());
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(track->getYear(), date.year());
EXPECT_EQ(track->getOriginalYear(), originalDate.year());
}
}
TEST_F(DatabaseFixture, Track_writtenAfter)
{
ScopedTrack track{ session, "MyTrack" };
const Wt::WDateTime dateTime{ Wt::WDate {1950, 1, 1}, Wt::WTime {12, 30, 20} };
{
auto transaction{ session.createWriteTransaction() };
track.get().modify()->setLastWriteTime(dateTime);
}
{
auto transaction{ session.createReadTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters {}) };
EXPECT_EQ(tracks.results.size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setWrittenAfter(dateTime.addSecs(-1))) };
EXPECT_EQ(tracks.results.size(), 1);
}
{
auto transaction{ session.createReadTransaction() };
const auto tracks{ Track::findIds(session, Track::FindParameters {}.setWrittenAfter(dateTime.addSecs(+1))) };
EXPECT_EQ(tracks.results.size(), 0);
}
}
}
+34 -33
View File
@@ -21,46 +21,47 @@
#include "database/TrackBookmark.hpp"
using ScopedTrackBookmark = ScopedEntity<Database::TrackBookmark>;
using namespace Database;
TEST_F(DatabaseFixture, TrackBookmark)
namespace lms::db::tests
{
ScopedTrack track {session, "MyTrack"};
ScopedUser user {session, "MyUser"};
using ScopedTrackBookmark = ScopedEntity<db::TrackBookmark>;
{
auto transaction {session.createReadTransaction()};
EXPECT_EQ(TrackBookmark::getCount(session), 0);
}
TEST_F(DatabaseFixture, TrackBookmark)
{
ScopedTrack track{ session, "MyTrack" };
ScopedUser user{ session, "MyUser" };
ScopedTrackBookmark bookmark {session, user.lockAndGet(), track.lockAndGet()};
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackBookmark::getCount(session), 0);
}
{
auto transaction {session.createWriteTransaction()};
ScopedTrackBookmark bookmark{ session, user.lockAndGet(), track.lockAndGet() };
bookmark.get().modify()->setComment("MyComment");
bookmark.get().modify()->setOffset(std::chrono::milliseconds {5});
}
{
auto transaction{ session.createWriteTransaction() };
{
auto transaction {session.createReadTransaction()};
bookmark.get().modify()->setComment("MyComment");
bookmark.get().modify()->setOffset(std::chrono::milliseconds{ 5 });
}
EXPECT_EQ(TrackBookmark::getCount(session), 1);
{
auto transaction{ session.createReadTransaction() };
const auto bookmarks {TrackBookmark::find(session, user.getId())};
ASSERT_EQ(bookmarks.results.size(), 1);
EXPECT_EQ(bookmarks.results.front(), bookmark.getId());
}
{
auto transaction {session.createReadTransaction()};
EXPECT_EQ(TrackBookmark::getCount(session), 1);
auto userBookmark {TrackBookmark::find(session, user.getId(), track.getId())};
ASSERT_TRUE(userBookmark);
EXPECT_EQ(userBookmark, bookmark.get());
const auto bookmarks{ TrackBookmark::find(session, user.getId()) };
ASSERT_EQ(bookmarks.results.size(), 1);
EXPECT_EQ(bookmarks.results.front(), bookmark.getId());
}
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(userBookmark->getOffset(), std::chrono::milliseconds {5});
EXPECT_EQ(userBookmark->getComment(), "MyComment");
}
}
auto userBookmark{ TrackBookmark::find(session, user.getId(), track.getId()) };
ASSERT_TRUE(userBookmark);
EXPECT_EQ(userBookmark, bookmark.get());
EXPECT_EQ(userBookmark->getOffset(), std::chrono::milliseconds{ 5 });
EXPECT_EQ(userBookmark->getComment(), "MyComment");
}
}
}
+21 -20
View File
@@ -21,28 +21,29 @@
#include "database/TrackFeatures.hpp"
using ScopedTrackFeatures = ScopedEntity<Database::TrackFeatures>;
using namespace Database;
TEST_F(DatabaseFixture, TrackFeatures)
namespace lms::db::tests
{
ScopedTrack track {session, "MyTrack"};
ScopedUser user {session, "MyUser"};
using ScopedTrackFeatures = ScopedEntity<db::TrackFeatures>;
{
auto transaction {session.createReadTransaction()};
EXPECT_EQ(TrackFeatures::getCount(session), 0);
}
TEST_F(DatabaseFixture, TrackFeatures)
{
ScopedTrack track{ session, "MyTrack" };
ScopedUser user{ session, "MyUser" };
ScopedTrackFeatures trackFeatures {session, track.lockAndGet(), ""};
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackFeatures::getCount(session), 0);
}
{
auto transaction {session.createWriteTransaction()};
EXPECT_EQ(TrackFeatures::getCount(session), 1);
ScopedTrackFeatures trackFeatures{ session, track.lockAndGet(), "" };
auto allTrackFeatures {TrackFeatures::find(session)};
ASSERT_EQ(allTrackFeatures.results.size(), 1);
EXPECT_EQ(allTrackFeatures.results.front(), trackFeatures.getId());
}
}
{
auto transaction{ session.createWriteTransaction() };
EXPECT_EQ(TrackFeatures::getCount(session), 1);
auto allTrackFeatures{ TrackFeatures::find(session) };
ASSERT_EQ(allTrackFeatures.results.size(), 1);
EXPECT_EQ(allTrackFeatures.results.front(), trackFeatures.getId());
}
}
}
+172 -171
View File
@@ -21,204 +21,205 @@
#include "Common.hpp"
using namespace Database;
TEST_F(DatabaseFixture, SingleTrackList)
namespace lms::db::tests
{
ScopedUser user{ session, "MyUser" };
TEST_F(DatabaseFixture, SingleTrackList)
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackList::getCount(session), 0);
ScopedUser user{ session, "MyUser" };
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackList::getCount(session), 0);
}
ScopedTrackList trackList{ session, "MytrackList", TrackListType::Playlist, false, user.lockAndGet() };
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackList::getCount(session), 1);
}
}
ScopedTrackList trackList{ session, "MytrackList", TrackListType::Playlist, false, user.lockAndGet() };
TEST_F(DatabaseFixture, SingleTrackListSingleTrack)
{
auto transaction{ session.createReadTransaction() };
EXPECT_EQ(TrackList::getCount(session), 1);
}
}
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList1{ session, "MyTrackList1", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrackList trackList2{ session, "MyTrackList2", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrack track{ session, "MyTrack" };
TEST_F(DatabaseFixture, SingleTrackListSingleTrack)
{
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList1{ session, "MyTrackList1", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrackList trackList2{ session, "MyTrackList2", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrack track{ session, "MyTrack" };
{
auto transaction{ session.createReadTransaction() };
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setTrackList(trackList1.getId())) };
EXPECT_EQ(tracks.results.size(), 0);
auto tracks{ Track::findIds(session, Track::FindParameters {}.setTrackList(trackList1.getId())) };
EXPECT_EQ(tracks.results.size(), 0);
tracks = Track::findIds(session, Track::FindParameters{}.setTrackList(trackList2.getId()));
EXPECT_EQ(tracks.results.size(), 0);
}
tracks = Track::findIds(session, Track::FindParameters{}.setTrackList(trackList2.getId()));
EXPECT_EQ(tracks.results.size(), 0);
{
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(track.get(), trackList1.get());
}
{
auto transaction{ session.createReadTransaction() };
auto tracks{ Track::findIds(session, Track::FindParameters {}.setTrackList(trackList1.getId())) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track.getId());
tracks = Track::findIds(session, Track::FindParameters{}.setTrackList(trackList2.getId()));
EXPECT_EQ(tracks.results.size(), 0);
}
}
TEST_F(DatabaseFixture, TrackList_SortMethod)
{
auto transaction{ session.createWriteTransaction() };
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList2{ session, "MyTrackList2", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrackList trackList1{ session, "MyTrackList1", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrack track{ session, "MyTrack" };
session.create<TrackListEntry>(track.get(), trackList1.get());
{
auto transaction{ session.createReadTransaction() };
const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::Name)) };
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList1.getId());
EXPECT_EQ(trackLists.results[1], trackList2.getId());
}
{
auto transaction{ session.createWriteTransaction() };
trackList1.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,1} });
trackList2.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,2} });
}
{
auto transaction{ session.createReadTransaction() };
const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc)) };
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList2.getId());
EXPECT_EQ(trackLists.results[1], trackList1.getId());
}
{
auto transaction{ session.createWriteTransaction() };
trackList1.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,2} });
trackList2.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,1} });
}
{
auto transaction{ session.createReadTransaction() };
const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc)) };
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList1.getId());
EXPECT_EQ(trackLists.results[1], trackList2.getId());
}
}
TEST_F(DatabaseFixture, SingleTrackListMultipleTrack)
{
auto transaction{ session.createReadTransaction() };
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList{ session, "MytrackList", TrackListType::Playlist, false, user.lockAndGet() };
std::list<ScopedTrack> tracks;
auto tracks{ Track::findIds(session, Track::FindParameters {}.setTrackList(trackList1.getId())) };
ASSERT_EQ(tracks.results.size(), 1);
EXPECT_EQ(tracks.results.front(), track.getId());
for (std::size_t i{}; i < 10; ++i)
{
tracks.emplace_back(session, "MyTrack" + std::to_string(i));
tracks = Track::findIds(session, Track::FindParameters{}.setTrackList(trackList2.getId()));
EXPECT_EQ(tracks.results.size(), 0);
}
}
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(tracks.back().get(), trackList.get());
}
TEST_F(DatabaseFixture, TrackList_SortMethod)
{
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList2{ session, "MyTrackList2", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrackList trackList1{ session, "MyTrackList1", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrack track{ session, "MyTrack" };
{
auto transaction{ session.createReadTransaction() };
{
auto transaction{ session.createReadTransaction() };
ASSERT_EQ(trackList->getCount(), tracks.size());
const auto trackIds{ trackList->getTrackIds() };
ASSERT_EQ(trackIds.size(), tracks.size());
const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::Name)) };
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList1.getId());
EXPECT_EQ(trackLists.results[1], trackList2.getId());
// Same order
std::size_t i{};
for (const ScopedTrack& track : tracks)
EXPECT_EQ(track.getId(), trackIds[i++]);
}
}
TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster)
{
auto transaction{ session.createWriteTransaction() };
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList1{ session, "MyTrackList1", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrackList trackList2{ session, "MyTrackList2", TrackListType::Playlist, false, user.lockAndGet() };
ScopedClusterType clusterType{ session, "MyClusterType" };
ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" };
ScopedTrack track{ session, "MyTrack" };
trackList1.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,1} });
trackList2.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,2} });
{
auto transaction{ session.createReadTransaction() };
auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) };
EXPECT_EQ(trackLists.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(track.get(), trackList1.get());
cluster.get().modify()->addTrack(track.get());
}
{
auto transaction{ session.createReadTransaction() };
auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) };
ASSERT_EQ(trackLists.results.size(), 1);
EXPECT_EQ(trackLists.results.front(), trackList1.getId());
}
}
TEST_F(DatabaseFixture, SingleTrackList_getEntries)
{
auto transaction{ session.createReadTransaction() };
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList{ session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrack track1{ session, "MyTrack" };
ScopedTrack track2{ session, "MyTrack" };
const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc)) };
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList2.getId());
EXPECT_EQ(trackLists.results[1], trackList1.getId());
{
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(track1.get(), trackList.get());
}
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries() };
ASSERT_EQ(entries.size(), 1);
EXPECT_EQ(entries.front()->getTrack()->getId(), track1.getId());
}
{
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(track2.get(), trackList.get());
}
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries() };
ASSERT_EQ(entries.size(), 2);
EXPECT_EQ(entries[0]->getTrack()->getId(), track1.getId());
EXPECT_EQ(entries[1]->getTrack()->getId(), track2.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries(Range {1, 1}) };
ASSERT_EQ(entries.size(), 1);
EXPECT_EQ(entries[0]->getTrack()->getId(), track2.getId());
}
}
{
auto transaction{ session.createWriteTransaction() };
trackList1.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,2} });
trackList2.get().modify()->setLastModifiedDateTime(Wt::WDateTime{ Wt::WDate {1900,1,1} });
}
{
auto transaction{ session.createReadTransaction() };
const auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setSortMethod(TrackListSortMethod::LastModifiedDesc)) };
ASSERT_EQ(trackLists.results.size(), 2);
EXPECT_EQ(trackLists.results[0], trackList1.getId());
EXPECT_EQ(trackLists.results[1], trackList2.getId());
}
}
TEST_F(DatabaseFixture, SingleTrackListMultipleTrack)
{
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList{ session, "MytrackList", TrackListType::Playlist, false, user.lockAndGet() };
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() };
session.create<TrackListEntry>(tracks.back().get(), trackList.get());
}
{
auto transaction{ session.createReadTransaction() };
ASSERT_EQ(trackList->getCount(), tracks.size());
const auto trackIds{ trackList->getTrackIds() };
ASSERT_EQ(trackIds.size(), tracks.size());
// Same order
std::size_t i{};
for (const ScopedTrack& track : tracks)
EXPECT_EQ(track.getId(), trackIds[i++]);
}
}
TEST_F(DatabaseFixture, SingleTrackListSingleTrackWithCluster)
{
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList1{ session, "MyTrackList1", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrackList trackList2{ session, "MyTrackList2", TrackListType::Playlist, false, user.lockAndGet() };
ScopedClusterType clusterType{ session, "MyClusterType" };
ScopedCluster cluster{ session, clusterType.lockAndGet(), "MyCluster" };
ScopedTrack track{ session, "MyTrack" };
{
auto transaction{ session.createReadTransaction() };
auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) };
EXPECT_EQ(trackLists.results.size(), 0);
}
{
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(track.get(), trackList1.get());
cluster.get().modify()->addTrack(track.get());
}
{
auto transaction{ session.createReadTransaction() };
auto trackLists{ TrackList::find(session, TrackList::FindParameters {}.setClusters({cluster.getId()})) };
ASSERT_EQ(trackLists.results.size(), 1);
EXPECT_EQ(trackLists.results.front(), trackList1.getId());
}
}
TEST_F(DatabaseFixture, SingleTrackList_getEntries)
{
ScopedUser user{ session, "MyUser" };
ScopedTrackList trackList{ session, "MyTrackList", TrackListType::Playlist, false, user.lockAndGet() };
ScopedTrack track1{ session, "MyTrack" };
ScopedTrack track2{ session, "MyTrack" };
{
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(track1.get(), trackList.get());
}
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries() };
ASSERT_EQ(entries.size(), 1);
EXPECT_EQ(entries.front()->getTrack()->getId(), track1.getId());
}
{
auto transaction{ session.createWriteTransaction() };
session.create<TrackListEntry>(track2.get(), trackList.get());
}
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries() };
ASSERT_EQ(entries.size(), 2);
EXPECT_EQ(entries[0]->getTrack()->getId(), track1.getId());
EXPECT_EQ(entries[1]->getTrack()->getId(), track2.getId());
}
{
auto transaction{ session.createReadTransaction() };
auto entries{ trackList.get()->getEntries(Range {1, 1}) };
ASSERT_EQ(entries.size(), 1);
EXPECT_EQ(entries[0]->getTrack()->getId(), track2.getId());
}
}
}