diff options
author | Adam Szmigin <smidge@xsco.net> | 2019-08-26 22:27:25 +0100 |
---|---|---|
committer | Adam Szmigin <smidge@xsco.net> | 2019-08-26 22:27:25 +0100 |
commit | 1283369dccfe904bb1c42941c37434864792e23e (patch) | |
tree | 9655114e5c84299a7e6b6bf8adf178203af7936b | |
parent | 855f9e50f8a0c19ac4827e913cfd1790fdb0158a (diff) |
New methods for crate lookup and creation.
-rw-r--r-- | include/djinterop/crate.hpp | 26 | ||||
-rw-r--r-- | include/djinterop/database.hpp | 10 | ||||
-rw-r--r-- | include/djinterop/exceptions.hpp | 20 | ||||
-rw-r--r-- | include/djinterop/track.hpp | 13 | ||||
-rw-r--r-- | src/djinterop/crate.cpp | 16 | ||||
-rw-r--r-- | src/djinterop/database.cpp | 10 | ||||
-rw-r--r-- | src/djinterop/enginelibrary/el_crate_impl.cpp | 108 | ||||
-rw-r--r-- | src/djinterop/enginelibrary/el_crate_impl.hpp | 3 | ||||
-rw-r--r-- | src/djinterop/enginelibrary/el_database_impl.cpp | 37 | ||||
-rw-r--r-- | src/djinterop/enginelibrary/el_database_impl.hpp | 4 | ||||
-rw-r--r-- | src/djinterop/enginelibrary/el_track_impl.cpp | 8 | ||||
-rw-r--r-- | src/djinterop/impl/crate_impl.hpp | 4 | ||||
-rw-r--r-- | src/djinterop/impl/database_impl.hpp | 6 | ||||
-rw-r--r-- | test/enginelibrary/crate_test.cpp | 89 | ||||
-rw-r--r-- | test/enginelibrary/performance_data_test.cpp | 56 | ||||
-rw-r--r-- | test/enginelibrary/track_test.cpp | 1 |
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); } + |