summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAdam Szmigin <smidge@xsco.net>2019-08-26 22:27:25 +0100
committerAdam Szmigin <smidge@xsco.net>2019-08-26 22:27:25 +0100
commit1283369dccfe904bb1c42941c37434864792e23e (patch)
tree9655114e5c84299a7e6b6bf8adf178203af7936b
parent855f9e50f8a0c19ac4827e913cfd1790fdb0158a (diff)
New methods for crate lookup and creation.
-rw-r--r--include/djinterop/crate.hpp26
-rw-r--r--include/djinterop/database.hpp10
-rw-r--r--include/djinterop/exceptions.hpp20
-rw-r--r--include/djinterop/track.hpp13
-rw-r--r--src/djinterop/crate.cpp16
-rw-r--r--src/djinterop/database.cpp10
-rw-r--r--src/djinterop/enginelibrary/el_crate_impl.cpp108
-rw-r--r--src/djinterop/enginelibrary/el_crate_impl.hpp3
-rw-r--r--src/djinterop/enginelibrary/el_database_impl.cpp37
-rw-r--r--src/djinterop/enginelibrary/el_database_impl.hpp4
-rw-r--r--src/djinterop/enginelibrary/el_track_impl.cpp8
-rw-r--r--src/djinterop/impl/crate_impl.hpp4
-rw-r--r--src/djinterop/impl/database_impl.hpp6
-rw-r--r--test/enginelibrary/crate_test.cpp89
-rw-r--r--test/enginelibrary/performance_data_test.cpp56
-rw-r--r--test/enginelibrary/track_test.cpp1
16 files changed, 357 insertions, 54 deletions
diff --git a/include/djinterop/crate.hpp b/include/djinterop/crate.hpp
index de0df74..94f73b8 100644
--- a/include/djinterop/crate.hpp
+++ b/include/djinterop/crate.hpp
@@ -59,11 +59,22 @@ public:
/// Copy assignment operator
crate& operator=(const crate& other) noexcept;
- /// Adds a track to the crate
+ /// Adds a track to the crate.
+ void add_track(int64_t track_id) const;
+
+ /// Adds a track to the crate.
///
/// A track can be contained in arbitrarily many (including zero) crates.
void add_track(track tr) const;
+ /// Add a range of tracks to the crate.
+ template <typename InputIterator>
+ void add_tracks(InputIterator first, InputIterator last)
+ {
+ for (auto iter = first; iter != last; ++iter)
+ add_track(*iter);
+ }
+
/// Returns the (direct) children of this crate
std::vector<crate> children() const;
@@ -73,6 +84,9 @@ public:
/// zero crates.
void clear_tracks() const;
+ /// Creates a new, empty crate as a child of this one, and returns it.
+ crate create_sub_crate(std::string name);
+
/// Returns the database containing the crate
database db() const;
@@ -95,7 +109,7 @@ public:
/// Returns the parent crate, if this crate has one
///
- /// If the crate doesn't have a parent, then `nullopt_t` is returned.
+ /// If the crate doesn't have a parent, then `nullopt` is returned.
std::experimental::optional<crate> parent() const;
/// Removes a track from the crate
@@ -109,10 +123,16 @@ public:
/// Sets this crate's parent
///
- /// If `nullopt_t` is given, then this crate will have no parent. That is,
+ /// If `nullopt` is given, then this crate will have no parent. That is,
/// it becomes a root crate.
void set_parent(std::experimental::optional<crate> parent) const;
+ /// Gets the sub-crate of this one with a given name.
+ ///
+ /// If no such crate is found, then `nullopt` is returned.
+ std::experimental::optional<crate> sub_crate_by_name(
+ const std::string& name) const;
+
/// Returns the crate's contained tracks
std::vector<track> tracks() const;
diff --git a/include/djinterop/database.hpp b/include/djinterop/database.hpp
index ea2d8ec..f52f9d0 100644
--- a/include/djinterop/database.hpp
+++ b/include/djinterop/database.hpp
@@ -75,10 +75,10 @@ public:
/// Returns all crates with the given name
std::vector<crate> crates_by_name(const std::string& name) const;
- /// Creates a new crate with the given name
+ /// Creates a new root crate with the given name.
///
/// The created crate has no parent.
- crate create_crate(std::string name) const;
+ crate create_root_crate(std::string name) const;
/// Creates a new track associated to a given music file
///
@@ -118,6 +118,12 @@ public:
/// All handles to that track become invalid.
void remove_track(track tr) const;
+ /// Returns the root-level crate with the given name.
+ ///
+ /// If no such crate exists, then `nullopt` is returned.
+ std::experimental::optional<crate> root_crate_by_name(
+ const std::string& name) const;
+
/// Returns all root crates contained in the database
///
/// A root crate is a crate that has no parent.
diff --git a/include/djinterop/exceptions.hpp b/include/djinterop/exceptions.hpp
index 65c774c..4386dc4 100644
--- a/include/djinterop/exceptions.hpp
+++ b/include/djinterop/exceptions.hpp
@@ -103,6 +103,26 @@ private:
int64_t id_;
};
+/// The `crate_database_inconsistency` exception is thrown when a database
+/// inconsistency is found that correlates to a crate.
+class crate_invalid_name : public std::runtime_error
+{
+public:
+ /// Construct the exception for a given crate name
+ explicit crate_invalid_name(
+ const std::string& what_arg, std::string name) noexcept
+ : runtime_error{what_arg.c_str()},
+ name_{name}
+ {
+ }
+
+ /// Get the name that was deemed invalid
+ std::string name() const noexcept { return name_; }
+
+private:
+ std::string name_;
+};
+
/// The `track_deleted` exception is thrown when an invalid `track` object is
/// used, i.e. one that does not exist in the database anymore.
class track_deleted : public std::invalid_argument
diff --git a/include/djinterop/track.hpp b/include/djinterop/track.hpp
index a7db114..1e625d6 100644
--- a/include/djinterop/track.hpp
+++ b/include/djinterop/track.hpp
@@ -40,15 +40,28 @@ class database;
class crate;
class track_impl;
+/// The `track_import_info` struct holds information about a track in a
+/// different, external Engine Library database. This can be associated with a
+/// track if it was imported into the current database from another one.
class track_import_info
{
public:
track_import_info() noexcept;
track_import_info(
std::string external_db_uuid, int64_t externl_track_id) noexcept;
+
+ /// Gets the UUID of the external Engine Library database.
std::string& external_db_uuid();
+
+ /// Gets a reference to a field holding the UUID of the external Engine
+ /// Library database.
const std::string& external_db_uuid() const;
+
+ /// Gets the id of the track in the external Engine Library database.
int64_t& external_track_id();
+
+ /// Gets a reference to a field holding the id of the track in the external
+ /// Engine Library database.
const int64_t& external_track_id() const;
private:
diff --git a/src/djinterop/crate.cpp b/src/djinterop/crate.cpp
index 3a0cc3a..6bb21b0 100644
--- a/src/djinterop/crate.cpp
+++ b/src/djinterop/crate.cpp
@@ -30,6 +30,11 @@ crate::~crate() = default;
crate& crate::operator=(const crate& other) noexcept = default;
+void crate::add_track(int64_t track_id) const
+{
+ pimpl_->add_track(track_id);
+}
+
void crate::add_track(track tr) const
{
pimpl_->add_track(tr);
@@ -45,6 +50,11 @@ void crate::clear_tracks() const
pimpl_->clear_tracks();
}
+crate crate::create_sub_crate(std::string name)
+{
+ return pimpl_->create_sub_crate(name);
+}
+
database crate::db() const
{
return pimpl_->db();
@@ -90,6 +100,12 @@ void crate::set_parent(std::experimental::optional<crate> parent) const
pimpl_->set_parent(to_boost_optional(parent));
}
+std::experimental::optional<crate> crate::sub_crate_by_name(
+ const std::string& name) const
+{
+ return from_boost_optional(pimpl_->sub_crate_by_name(name));
+}
+
std::vector<track> crate::tracks() const
{
return pimpl_->tracks();
diff --git a/src/djinterop/database.cpp b/src/djinterop/database.cpp
index bd26a01..2dd8e73 100644
--- a/src/djinterop/database.cpp
+++ b/src/djinterop/database.cpp
@@ -52,9 +52,9 @@ std::vector<crate> database::crates_by_name(const std::string& name) const
return pimpl_->crates_by_name(name);
}
-crate database::create_crate(std::string name) const
+crate database::create_root_crate(std::string name) const
{
- return pimpl_->create_crate(name);
+ return pimpl_->create_root_crate(name);
}
track database::create_track(std::string relative_path) const
@@ -92,6 +92,12 @@ std::vector<crate> database::root_crates() const
return pimpl_->root_crates();
}
+std::experimental::optional<crate> database::root_crate_by_name(
+ const std::string& name) const
+{
+ return from_boost_optional(pimpl_->root_crate_by_name(name));
+}
+
std::experimental::optional<track> database::track_by_id(int64_t id) const
{
return from_boost_optional(pimpl_->track_by_id(id));
diff --git a/src/djinterop/enginelibrary/el_crate_impl.cpp b/src/djinterop/enginelibrary/el_crate_impl.cpp
index be80a05..3403fee 100644
--- a/src/djinterop/enginelibrary/el_crate_impl.cpp
+++ b/src/djinterop/enginelibrary/el_crate_impl.cpp
@@ -31,8 +31,30 @@ namespace enginelibrary
using djinterop::crate;
using djinterop::track;
+// Note that crates in the Engine Library format may exist either at top/root
+// level, or be sub-crates underneath another crate. This information is
+// encoded redundantly in multiple places in the EL database schema:
+//
+// * Crate (id, title, path)
+// The `path` field is a semicolon-delimited string of crate titles,
+// representing the path from the root to the current crate. Note that
+// there is always an additional trailing semicolon in this field. As such,
+// semicolon is a prohibited character in crate names.
+//
+// * CrateParentList (crateOriginId, crateParentId)
+// Every crate is specified as having precisely one immediate parent. A
+// top-level crate is said to have itself as parent. The crate id is
+// written to the `crateOriginId` field, and the parent (or itself) is
+// written to the `crateParentId` field.
+//
+// * CrateHierarchy (crateId, crateIdChild)
+// The denormalised/flattened inheritance hierarchy is written to this
+// table, whereby the id of every descendant (not child) of a crate is
+// written to the `crateIdChild` field. Note that the reflexive
+// relationship is not written to this table.
namespace
{
+
void update_path(
sqlite::database& music_db, crate cr, const std::string& parent_path)
{
@@ -47,6 +69,20 @@ void update_path(
}
}
+void ensure_valid_name(const std::string& name)
+{
+ if (name == "")
+ {
+ throw djinterop::crate_invalid_name{
+ "Crate names must be non-empty", name};
+ }
+ else if (name.find_first_of(';') != std::string::npos)
+ {
+ throw djinterop::crate_invalid_name{
+ "Crate names must not contain semicolons", name};
+ }
+}
+
} // namespace
el_crate_impl::el_crate_impl(std::shared_ptr<el_storage> storage, int64_t id)
@@ -54,21 +90,26 @@ el_crate_impl::el_crate_impl(std::shared_ptr<el_storage> storage, int64_t id)
{
}
-void el_crate_impl::add_track(track tr)
+void el_crate_impl::add_track(int64_t track_id)
{
el_transaction_guard_impl trans{storage_};
storage_->db
<< "DELETE FROM CrateTrackList WHERE crateId = ? AND trackId = ?"
- << id() << tr.id();
+ << id() << track_id;
storage_->db
<< "INSERT INTO CrateTrackList (crateId, trackId) VALUES (?, ?)" << id()
- << tr.id();
+ << track_id;
trans.commit();
}
+void el_crate_impl::add_track(track tr)
+{
+ add_track(tr.id());
+}
+
std::vector<crate> el_crate_impl::children()
{
std::vector<crate> results;
@@ -86,6 +127,51 @@ void el_crate_impl::clear_tracks()
storage_->db << "DELETE FROM CrateTrackList WHERE crateId = ?" << id();
}
+crate el_crate_impl::create_sub_crate(std::string name)
+{
+ ensure_valid_name(name);
+ el_transaction_guard_impl trans{storage_};
+
+ std::string path;
+ storage_->db
+ << "SELECT path FROM Crate WHERE id = ?"
+ << id() >>
+ [&](std::string path_val) {
+ if (path.empty())
+ {
+ path = std::move(path_val);
+ }
+ else
+ {
+ throw crate_database_inconsistency{
+ "More than one crate for the same id", id()};
+ }
+ };
+
+ storage_->db << "INSERT INTO Crate (title, path) VALUES (?, ?)"
+ << name.data() << (path + name + ";");
+
+ int64_t sub_id = storage_->db.last_insert_rowid();
+
+ storage_->db << "INSERT INTO CrateParentList (crateOriginId, "
+ "crateParentId) VALUES (?, ?)"
+ << sub_id << id();
+
+ storage_->db
+ << "INSERT INTO CrateHierarchy (crateId, crateIdChild) "
+ "SELECT crateId, ? FROM CrateHierarchy "
+ "WHERE crateIdChild = ? "
+ "UNION "
+ "SELECT ? AS crateId, ? AS crateIdChild"
+ << sub_id << id() << id() << sub_id;
+
+ crate cr{std::make_shared<el_crate_impl>(storage_, sub_id)};
+
+ trans.commit();
+
+ return cr;
+}
+
database el_crate_impl::db()
{
return database{std::make_shared<el_database_impl>(storage_)};
@@ -176,6 +262,7 @@ void el_crate_impl::remove_track(track tr)
void el_crate_impl::set_name(std::string name)
{
+ ensure_valid_name(name);
el_transaction_guard_impl trans{storage_};
// obtain parent's `path`
@@ -236,6 +323,21 @@ void el_crate_impl::set_parent(boost::optional<crate> parent)
trans.commit();
}
+boost::optional<crate> el_crate_impl::sub_crate_by_name(const std::string& name)
+{
+ boost::optional<crate> cr;
+ storage_->db << "SELECT cr.id FROM Crate cr "
+ "JOIN CrateParentList cpl ON (cpl.crateOriginId = cr.id) "
+ "WHERE cr.title = ? "
+ "AND cpl.crateParentId = ? "
+ "ORDER BY cr.id"
+ << name.data() << id() >>
+ [&](int64_t id) {
+ cr = crate{std::make_shared<el_crate_impl>(storage_, id)};
+ };
+ return cr;
+}
+
std::vector<track> el_crate_impl::tracks()
{
std::vector<track> results;
diff --git a/src/djinterop/enginelibrary/el_crate_impl.hpp b/src/djinterop/enginelibrary/el_crate_impl.hpp
index cdca83a..9bf657c 100644
--- a/src/djinterop/enginelibrary/el_crate_impl.hpp
+++ b/src/djinterop/enginelibrary/el_crate_impl.hpp
@@ -32,9 +32,11 @@ class el_crate_impl : public djinterop::crate_impl
public:
el_crate_impl(std::shared_ptr<el_storage> storage, int64_t id);
+ void add_track(int64_t track_id) override;
void add_track(track tr) override;
std::vector<crate> children() override;
void clear_tracks() override;
+ crate create_sub_crate(std::string name) override;
database db() override;
std::vector<crate> descendants() override;
bool is_valid() override;
@@ -43,6 +45,7 @@ public:
void remove_track(track tr) override;
void set_name(std::string name) override;
void set_parent(boost::optional<crate> parent) override;
+ boost::optional<crate> sub_crate_by_name(const std::string& name) override;
std::vector<track> tracks() override;
private:
diff --git a/src/djinterop/enginelibrary/el_database_impl.cpp b/src/djinterop/enginelibrary/el_database_impl.cpp
index 76d77a0..a9b6090 100644
--- a/src/djinterop/enginelibrary/el_database_impl.cpp
+++ b/src/djinterop/enginelibrary/el_database_impl.cpp
@@ -32,6 +32,25 @@ namespace enginelibrary
using djinterop::crate;
using djinterop::track;
+namespace
+{
+void ensure_valid_crate_name(const std::string& name)
+{
+ if (name == "")
+ {
+ throw djinterop::crate_invalid_name{
+ "Crate names must be non-empty", name};
+ }
+ else if (name.find_first_of(';') != std::string::npos)
+ {
+ throw djinterop::crate_invalid_name{
+ "Crate names must not contain semicolons", name};
+ }
+}
+
+} // namespace
+
+
el_database_impl::el_database_impl(std::string directory)
: storage_{std::make_shared<el_storage>(std::move(directory))}
{
@@ -90,8 +109,9 @@ std::vector<crate> el_database_impl::crates_by_name(const std::string& name)
return results;
}
-crate el_database_impl::create_crate(std::string name)
+crate el_database_impl::create_root_crate(std::string name)
{
+ ensure_valid_crate_name(name);
el_transaction_guard_impl trans{storage_};
storage_->db << "INSERT INTO Crate (title, path) VALUES (?, ?)"
@@ -238,6 +258,21 @@ std::vector<crate> el_database_impl::root_crates()
return results;
}
+boost::optional<crate> el_database_impl::root_crate_by_name(const std::string& name)
+{
+ boost::optional<crate> cr;
+ storage_->db << "SELECT cr.id FROM Crate cr "
+ "JOIN CrateParentList cpl ON (cpl.crateOriginId = cr.id) "
+ "WHERE cr.title = ? "
+ "AND cpl.crateOriginId = cpl.crateParentId "
+ "ORDER BY cr.id"
+ << name.data() >>
+ [&](int64_t id) {
+ cr = crate{std::make_shared<el_crate_impl>(storage_, id)};
+ };
+ return cr;
+}
+
boost::optional<track> el_database_impl::track_by_id(int64_t id)
{
boost::optional<track> tr;
diff --git a/src/djinterop/enginelibrary/el_database_impl.hpp b/src/djinterop/enginelibrary/el_database_impl.hpp
index 2502fd3..535ef20 100644
--- a/src/djinterop/enginelibrary/el_database_impl.hpp
+++ b/src/djinterop/enginelibrary/el_database_impl.hpp
@@ -37,7 +37,7 @@ public:
std::vector<djinterop::crate> crates() override;
std::vector<djinterop::crate> crates_by_name(
const std::string& name) override;
- djinterop::crate create_crate(std::string name) override;
+ djinterop::crate create_root_crate(std::string name) override;
track create_track(std::string relative_path) override;
std::string directory() override;
bool is_supported() override;
@@ -45,6 +45,8 @@ public:
void remove_crate(djinterop::crate cr) override;
void remove_track(djinterop::track tr) override;
std::vector<djinterop::crate> root_crates() override;
+ boost::optional<djinterop::crate> root_crate_by_name(
+ const std::string& name) override;
boost::optional<djinterop::track> track_by_id(int64_t id) override;
std::vector<djinterop::track> tracks() override;
std::vector<djinterop::track> tracks_by_relative_path(
diff --git a/src/djinterop/enginelibrary/el_track_impl.cpp b/src/djinterop/enginelibrary/el_track_impl.cpp
index fc71995..66acd29 100644
--- a/src/djinterop/enginelibrary/el_track_impl.cpp
+++ b/src/djinterop/enginelibrary/el_track_impl.cpp
@@ -431,7 +431,7 @@ void el_track_impl::set_hot_cue_at(int32_t index, boost::optional<hot_cue> cue)
auto quick_cues_d = get_quick_cues_data();
quick_cues_d.hot_cues[index] = std::move(cue);
set_quick_cues_data(std::move(quick_cues_d));
- storage_->db << "END";
+ trans.commit();
}
std::array<boost::optional<hot_cue>, 8> el_track_impl::hot_cues()
@@ -448,7 +448,7 @@ void el_track_impl::set_hot_cues(std::array<boost::optional<hot_cue>, 8> cues)
auto quick_cues_d = get_quick_cues_data();
quick_cues_d.hot_cues = std::move(cues);
set_quick_cues_data(std::move(quick_cues_d));
- storage_->db << "END";
+ trans.commit();
}
boost::optional<track_import_info> el_track_impl::import_info()
@@ -609,7 +609,7 @@ void el_track_impl::set_loop_at(int32_t index, boost::optional<loop> l)
auto loops_d = get_loops_data();
loops_d.loops[index] = std::move(l);
set_loops_data(std::move(loops_d));
- storage_->db << "END";
+ trans.commit();
}
std::array<boost::optional<loop>, 8> el_track_impl::loops()
@@ -624,7 +624,7 @@ void el_track_impl::set_loops(std::array<boost::optional<loop>, 8> cues)
loops_data loops_d;
loops_d.loops = std::move(cues);
set_loops_data(std::move(loops_d));
- storage_->db << "END";
+ trans.commit();
}
std::vector<waveform_entry> el_track_impl::overview_waveform()
diff --git a/src/djinterop/impl/crate_impl.hpp b/src/djinterop/impl/crate_impl.hpp
index 01c875a..8dd4f9d 100644
--- a/src/djinterop/impl/crate_impl.hpp
+++ b/src/djinterop/impl/crate_impl.hpp
@@ -37,15 +37,19 @@ public:
int64_t id() noexcept;
+ virtual void add_track(int64_t track_id) = 0;
virtual void add_track(track tr) = 0;
virtual std::vector<crate> children() = 0;
virtual void clear_tracks() = 0;
+ virtual crate create_sub_crate(std::string name) = 0;
virtual database db() = 0;
virtual std::vector<crate> descendants() = 0;
virtual bool is_valid() = 0;
virtual std::string name() = 0;
virtual boost::optional<crate> parent() = 0;
virtual void remove_track(track tr) = 0;
+ virtual boost::optional<crate> sub_crate_by_name(
+ const std::string& name) = 0;
virtual void set_name(std::string name) = 0;
virtual void set_parent(boost::optional<crate> parent) = 0;
virtual std::vector<track> tracks() = 0;
diff --git a/src/djinterop/impl/database_impl.hpp b/src/djinterop/impl/database_impl.hpp
index b6a883f..304b5bd 100644
--- a/src/djinterop/impl/database_impl.hpp
+++ b/src/djinterop/impl/database_impl.hpp
@@ -38,7 +38,7 @@ public:
virtual boost::optional<crate> crate_by_id(int64_t id) = 0;
virtual std::vector<crate> crates() = 0;
virtual std::vector<crate> crates_by_name(const std::string& name) = 0;
- virtual crate create_crate(std::string name) = 0;
+ virtual crate create_root_crate(std::string name) = 0;
virtual track create_track(std::string relative_path) = 0;
virtual std::string directory() = 0;
virtual bool is_supported() = 0;
@@ -46,10 +46,12 @@ public:
virtual void remove_crate(crate cr) = 0;
virtual void remove_track(track tr) = 0;
virtual std::vector<crate> root_crates() = 0;
+ virtual boost::optional<crate> root_crate_by_name(
+ const std::string& name) = 0;
virtual boost::optional<track> track_by_id(int64_t id) = 0;
virtual std::vector<track> tracks() = 0;
virtual std::vector<track> tracks_by_relative_path(
- const std::string& relative_path) = 0;
+ const std::string& relative_path) = 0;
virtual std::string uuid() = 0;
virtual semantic_version version() = 0;
};
diff --git a/test/enginelibrary/crate_test.cpp b/test/enginelibrary/crate_test.cpp
index 12081eb..313e056 100644
--- a/test/enginelibrary/crate_test.cpp
+++ b/test/enginelibrary/crate_test.cpp
@@ -28,6 +28,7 @@
#include <djinterop/crate.hpp>
#include <djinterop/database.hpp>
#include <djinterop/enginelibrary.hpp>
+#include <djinterop/exceptions.hpp>
#include <djinterop/track.hpp>
#define STRINGIFY(x) STRINGIFY_(x)
@@ -65,20 +66,6 @@ static void copy_test_db_to_temp_dir(const fs::path &temp_dir)
fs::copy_file(perfdata_db_path, temp_dir / perfdata_db_path.filename());
}
-static void populate_crate_1(djinterop::crate &c)
-{
- c.set_name("Foo Crate");
- c.set_parent({});
- c.clear_tracks();
-}
-
-static void populate_crate_2(djinterop::crate &c)
-{
- c.set_name("Bar Crate");
- c.set_parent({});
- c.clear_tracks();
-}
-
static void check_crate_2(djinterop::crate c)
{
BOOST_CHECK(c.is_valid());
@@ -168,13 +155,12 @@ BOOST_AUTO_TEST_CASE(ctor__nonexistent_crate__none)
BOOST_CHECK(!db.crate_by_id(123));
}
-BOOST_AUTO_TEST_CASE(save__new_crate_good_values__saves)
+BOOST_AUTO_TEST_CASE(create_root_crate__good_values__succeeds)
{
// Arrange/Act
auto temp_dir = create_temp_dir();
auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
- auto c = db.create_crate("");
- populate_crate_2(c);
+ auto c = db.create_root_crate("Bar Crate");
// Assert
BOOST_CHECK_NE(c.id(), 0);
@@ -184,13 +170,12 @@ BOOST_AUTO_TEST_CASE(save__new_crate_good_values__saves)
remove_temp_dir(temp_dir);
}
-BOOST_AUTO_TEST_CASE(ctor_copy__saved_track__zero_id_and_copied_fields)
+BOOST_AUTO_TEST_CASE(ctor_copy__saved_track__copied_fields)
{
// Arrange
auto temp_dir = create_temp_dir();
auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
- auto c = db.create_crate("");
- populate_crate_2(c);
+ auto c = db.create_root_crate("Bar Crate");
// Act
djinterop::crate copy{c};
@@ -202,15 +187,16 @@ BOOST_AUTO_TEST_CASE(ctor_copy__saved_track__zero_id_and_copied_fields)
remove_temp_dir(temp_dir);
}
-BOOST_AUTO_TEST_CASE(save__existing_track_good_values__saves)
+BOOST_AUTO_TEST_CASE(set_name__existing_track_good_values__saves)
{
- // Arrange/Act
+ // Arrange
auto temp_dir = create_temp_dir();
auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
- auto c = db.create_crate("");
- populate_crate_1(c);
+ auto c = db.create_root_crate("Foo Crate");
auto crate_id = c.id();
- populate_crate_2(c);
+
+ // Act
+ c.set_name("Bar Crate");
// Assert
BOOST_CHECK_EQUAL(c.id(), crate_id);
@@ -220,21 +206,18 @@ BOOST_AUTO_TEST_CASE(save__existing_track_good_values__saves)
remove_temp_dir(temp_dir);
}
-BOOST_AUTO_TEST_CASE(save__change_hierarchy__saves)
+BOOST_AUTO_TEST_CASE(set_parent__change_hierarchy__saves)
{
// Arrange
auto temp_dir = create_temp_dir();
auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
// Arrange a hierarchy of c1 (root) -> c2 -> c3
- auto c1 = db.create_crate("");
- auto c2 = db.create_crate("");
- auto c3 = db.create_crate("");
+ auto c1 = db.create_root_crate("Grandfather");
+ auto c2 = db.create_root_crate("Father");
+ auto c3 = db.create_root_crate("Son");
// Act
- c1.set_name("Grandfather");
- c2.set_name("Father");
c2.set_parent(c1);
- c3.set_name("Son");
c3.set_parent(c2);
// Change c2's parent
c2.set_parent({});
@@ -247,7 +230,7 @@ BOOST_AUTO_TEST_CASE(save__change_hierarchy__saves)
remove_temp_dir(temp_dir);
}
-BOOST_AUTO_TEST_CASE(save__add_tracks__saves)
+BOOST_AUTO_TEST_CASE(add_track__valid_track__saves)
{
// Arrange
auto temp_dir = create_temp_dir();
@@ -266,13 +249,12 @@ BOOST_AUTO_TEST_CASE(save__add_tracks__saves)
remove_temp_dir(temp_dir);
}
-BOOST_AUTO_TEST_CASE(op_copy_assign__saved_track__zero_id_and_copied_fields)
+BOOST_AUTO_TEST_CASE(op_copy_assign__saved_track__copied_fields)
{
// Arrange
auto temp_dir = create_temp_dir();
auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
- auto c = db.create_crate("");
- populate_crate_2(c);
+ auto c = db.create_root_crate("Bar Crate");
// Act
auto copy = c;
@@ -307,3 +289,38 @@ BOOST_AUTO_TEST_CASE(crate_by_name__invalid_crate__not_found)
// Assert
BOOST_CHECK(crates.empty());
}
+
+BOOST_AUTO_TEST_CASE(set_name__invalid_name__throws)
+{
+ // Arrange
+ auto temp_dir = create_temp_dir();
+ auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
+ auto c = db.create_root_crate("Root");
+
+ // Act/Assert
+ BOOST_CHECK_THROW(
+ c.set_name(""), djinterop::crate_invalid_name);
+ BOOST_CHECK_THROW(
+ c.set_name("Contains ; semicolon"), djinterop::crate_invalid_name);
+}
+
+BOOST_AUTO_TEST_CASE(create_sub_crate__valid_name__succeeds)
+{
+ // Arrange
+ auto temp_dir = create_temp_dir();
+ auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
+ auto c = db.create_root_crate("Root");
+
+ // Act
+ auto sc = c.create_sub_crate("Sub-Crate");
+
+ // Assert
+ auto parent = sc.parent();
+ BOOST_CHECK(parent.has_value());
+ BOOST_CHECK_EQUAL(parent.value().id(), c.id());
+ auto children = c.children();
+ BOOST_CHECK_EQUAL(children.size(), 1);
+ auto child = children[0];
+ BOOST_CHECK_EQUAL(child.id(), sc.id());
+}
+
diff --git a/test/enginelibrary/performance_data_test.cpp b/test/enginelibrary/performance_data_test.cpp
index ee0ca6a..e74d30f 100644
--- a/test/enginelibrary/performance_data_test.cpp
+++ b/test/enginelibrary/performance_data_test.cpp
@@ -61,6 +61,25 @@ static void remove_temp_dir(const fs::path& temp_dir)
static void populate_track_1(djinterop::track& t)
{
+ // Usual meta-data (not under test but for completeness).
+ t.set_track_number(1);
+ t.set_bpm(123);
+ t.set_year(2017);
+ t.set_title("Mad (Original Mix)");
+ t.set_artist("Dennis Cruz");
+ t.set_album("Mad EP");
+ t.set_genre("Tech House");
+ t.set_comment("Purchased at Beatport.com");
+ t.set_publisher("Stereo Productions");
+ t.set_composer(std::experimental::nullopt);
+ t.set_key(djinterop::musical_key::a_minor);
+ t.set_relative_path("../01 - Dennis Cruz - Mad (Original Mix).mp3");
+ t.set_last_modified_at(c::system_clock::time_point{c::seconds{1509371790}});
+ t.set_bitrate(320);
+ t.set_last_played_at(std::experimental::nullopt);
+ t.set_last_accessed_at(c::system_clock::time_point{c::seconds{1509321600}});
+ t.set_import_info(std::experimental::nullopt);
+
// Track data fields
t.set_sampling(djinterop::sampling_info{44100, 17452800});
t.set_key(djinterop::musical_key::a_minor);
@@ -199,6 +218,26 @@ static void check_track_1(const djinterop::track& t)
static void populate_track_2(djinterop::track& t)
{
+ // Usual meta-data (not under test but for completeness).
+ t.set_track_number(3);
+ t.set_bpm(128);
+ t.set_year(2018);
+ t.set_title("Made-up Track (Foo Bar Remix)");
+ t.set_artist("Not A Real Artist");
+ t.set_album("Fake Album");
+ t.set_genre("Progressive House");
+ t.set_comment("Comment goes here");
+ t.set_publisher("Here is the publisher text");
+ t.set_composer("And the composer text");
+ t.set_key(djinterop::musical_key::c_major);
+ t.set_relative_path(
+ "../03 - Not A Real Artist - Made-up Track (Foo Bar Remix).flac");
+ t.set_last_modified_at(c::system_clock::time_point{c::seconds{1517413933}});
+ t.set_bitrate(1411);
+ t.set_last_played_at(c::system_clock::time_point{c::seconds{1518739200}});
+ t.set_last_accessed_at(c::system_clock::time_point{c::seconds{1518815683}});
+ t.set_import_info({"e535b170-26ef-4f30-8cb2-5b9fa4c2a27f", 123});
+
// Track data fields
t.set_sampling(djinterop::sampling_info{48000, 10795393});
t.set_key(djinterop::musical_key::b_minor);
@@ -336,3 +375,20 @@ BOOST_AUTO_TEST_CASE(save__existing_track__saves)
check_track_2(t_reloaded);
remove_temp_dir(temp_dir);
}
+
+BOOST_AUTO_TEST_CASE(set_hot_cue_at__empty_track_valid_entries__succeeds)
+{
+ // Arrange
+ auto temp_dir = create_temp_dir();
+ auto db = el::create_database(temp_dir.string(), el::version_1_7_1);
+ auto t = db.create_track("");
+
+ // Act
+ djinterop::hot_cue hc{"My Cue", 12345.6789, el::standard_pad_colors::pad_3};
+ t.set_hot_cue_at(1, hc);
+
+ // Assert
+ auto hot_cues = t.hot_cues();
+ BOOST_CHECK(hot_cues[1]);
+}
+
diff --git a/test/enginelibrary/track_test.cpp b/test/enginelibrary/track_test.cpp
index 5694ac1..516b0af 100644
--- a/test/enginelibrary/track_test.cpp
+++ b/test/enginelibrary/track_test.cpp
@@ -311,3 +311,4 @@ BOOST_AUTO_TEST_CASE(op_copy_assign__saved_track__copied_fields)
check_track_1(copy);
remove_temp_dir(temp_dir);
}
+