You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
6.1 KiB
208 lines
6.1 KiB
/*
|
|
* Copyright 2023 jacqueline <me@jacqueline.id.au>
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-only
|
|
*/
|
|
|
|
#include "database.hpp"
|
|
|
|
#include <stdint.h>
|
|
#include <iomanip>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
#include "catch2/catch.hpp"
|
|
#include "driver_cache.hpp"
|
|
#include "esp_log.h"
|
|
#include "file_gatherer.hpp"
|
|
#include "i2c_fixture.hpp"
|
|
#include "leveldb/db.h"
|
|
#include "spi_fixture.hpp"
|
|
#include "tag_parser.hpp"
|
|
#include "track.hpp"
|
|
|
|
namespace database {
|
|
|
|
class TestBackends : public IFileGatherer, public ITagParser {
|
|
public:
|
|
std::map<std::string, TrackTags> tracks;
|
|
|
|
auto MakeTrack(const std::string& path, const std::string& title) -> void {
|
|
TrackTags tags;
|
|
tags.encoding = Encoding::kMp3;
|
|
tags.title = title;
|
|
tracks[path] = tags;
|
|
}
|
|
|
|
auto FindFiles(const std::string& root,
|
|
std::function<void(const std::string&)> cb) -> void override {
|
|
for (auto keyval : tracks) {
|
|
std::invoke(cb, keyval.first);
|
|
}
|
|
}
|
|
|
|
auto ReadAndParseTags(const std::string& path, TrackTags* out)
|
|
-> bool override {
|
|
if (tracks.contains(path)) {
|
|
*out = tracks.at(path);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
};
|
|
|
|
TEST_CASE("track database", "[integration]") {
|
|
I2CFixture i2c;
|
|
SpiFixture spi;
|
|
drivers::DriverCache drivers;
|
|
auto storage = drivers.AcquireStorage();
|
|
|
|
Database::Destroy();
|
|
|
|
TestBackends tracks;
|
|
auto open_res = Database::Open(&tracks, &tracks);
|
|
REQUIRE(open_res.has_value());
|
|
std::unique_ptr<Database> db(open_res.value());
|
|
|
|
SECTION("empty database") {
|
|
std::unique_ptr<Result<Track>> res(db->GetTracks(10).get());
|
|
REQUIRE(res->values().size() == 0);
|
|
}
|
|
|
|
SECTION("add new tracks") {
|
|
tracks.MakeTrack("track1.mp3", "Track 1");
|
|
tracks.MakeTrack("track2.wav", "Track 2");
|
|
tracks.MakeTrack("track3.exe", "Track 3");
|
|
|
|
db->Update();
|
|
|
|
std::unique_ptr<Result<Track>> res(db->GetTracks(10).get());
|
|
REQUIRE(res->values().size() == 3);
|
|
CHECK(*res->values().at(0).tags().title == "Track 1");
|
|
CHECK(res->values().at(0).data().id() == 1);
|
|
CHECK(*res->values().at(1).tags().title == "Track 2");
|
|
CHECK(res->values().at(1).data().id() == 2);
|
|
CHECK(*res->values().at(2).tags().title == "Track 3");
|
|
CHECK(res->values().at(2).data().id() == 3);
|
|
|
|
SECTION("update with no filesystem changes") {
|
|
db->Update();
|
|
|
|
std::unique_ptr<Result<Track>> new_res(db->GetTracks(10).get());
|
|
REQUIRE(new_res->values().size() == 3);
|
|
CHECK(res->values().at(0) == new_res->values().at(0));
|
|
CHECK(res->values().at(1) == new_res->values().at(1));
|
|
CHECK(res->values().at(2) == new_res->values().at(2));
|
|
}
|
|
|
|
SECTION("update with all tracks gone") {
|
|
tracks.tracks.clear();
|
|
|
|
db->Update();
|
|
|
|
std::unique_ptr<Result<Track>> new_res(db->GetTracks(10).get());
|
|
CHECK(new_res->values().size() == 0);
|
|
|
|
SECTION("update with one track returned") {
|
|
tracks.MakeTrack("track2.wav", "Track 2");
|
|
|
|
db->Update();
|
|
|
|
std::unique_ptr<Result<Track>> new_res(db->GetTracks(10).get());
|
|
REQUIRE(new_res->values().size() == 1);
|
|
CHECK(res->values().at(1) == new_res->values().at(0));
|
|
}
|
|
}
|
|
|
|
SECTION("update with one track gone") {
|
|
tracks.tracks.erase("track2.wav");
|
|
|
|
db->Update();
|
|
|
|
std::unique_ptr<Result<Track>> new_res(db->GetTracks(10).get());
|
|
REQUIRE(new_res->values().size() == 2);
|
|
CHECK(res->values().at(0) == new_res->values().at(0));
|
|
CHECK(res->values().at(2) == new_res->values().at(1));
|
|
}
|
|
|
|
SECTION("update with tags changed") {
|
|
tracks.MakeTrack("track3.exe", "The Track 3");
|
|
|
|
db->Update();
|
|
|
|
std::unique_ptr<Result<Track>> new_res(db->GetTracks(10).get());
|
|
REQUIRE(new_res->values().size() == 3);
|
|
CHECK(res->values().at(0) == new_res->values().at(0));
|
|
CHECK(res->values().at(1) == new_res->values().at(1));
|
|
CHECK(*new_res->values().at(2).tags().title == "The Track 3");
|
|
// The id should not have changed, since this was just a tag update.
|
|
CHECK(res->values().at(2).data().id() ==
|
|
new_res->values().at(2).data().id());
|
|
}
|
|
|
|
SECTION("update with one new track") {
|
|
tracks.MakeTrack("my track.midi", "Track 1 (nightcore remix)");
|
|
|
|
db->Update();
|
|
|
|
std::unique_ptr<Result<Track>> new_res(db->GetTracks(10).get());
|
|
REQUIRE(new_res->values().size() == 4);
|
|
CHECK(res->values().at(0) == new_res->values().at(0));
|
|
CHECK(res->values().at(1) == new_res->values().at(1));
|
|
CHECK(res->values().at(2) == new_res->values().at(2));
|
|
CHECK(*new_res->values().at(3).tags().title ==
|
|
"Track 1 (nightcore remix)");
|
|
CHECK(new_res->values().at(3).data().id() == 4);
|
|
}
|
|
|
|
SECTION("get tracks with pagination") {
|
|
std::unique_ptr<Result<Track>> res(db->GetTracks(1).get());
|
|
|
|
REQUIRE(res->values().size() == 1);
|
|
CHECK(res->values().at(0).data().id() == 1);
|
|
REQUIRE(res->next_page());
|
|
|
|
res.reset(db->GetPage(&res->next_page().value()).get());
|
|
|
|
REQUIRE(res->values().size() == 1);
|
|
CHECK(res->values().at(0).data().id() == 2);
|
|
REQUIRE(res->next_page());
|
|
|
|
res.reset(db->GetPage(&res->next_page().value()).get());
|
|
|
|
REQUIRE(res->values().size() == 1);
|
|
CHECK(res->values().at(0).data().id() == 3);
|
|
REQUIRE(!res->next_page());
|
|
|
|
SECTION("page backwards") {
|
|
REQUIRE(res->prev_page());
|
|
|
|
res.reset(db->GetPage(&res->prev_page().value()).get());
|
|
|
|
REQUIRE(res->values().size() == 1);
|
|
CHECK(res->values().at(0).data().id() == 2);
|
|
REQUIRE(res->prev_page());
|
|
|
|
res.reset(db->GetPage(&res->prev_page().value()).get());
|
|
|
|
REQUIRE(res->values().size() == 1);
|
|
CHECK(res->values().at(0).data().id() == 1);
|
|
REQUIRE(!res->prev_page());
|
|
|
|
SECTION("page forwards again") {
|
|
REQUIRE(res->next_page());
|
|
|
|
res.reset(db->GetPage(&res->next_page().value()).get());
|
|
|
|
REQUIRE(res->values().size() == 1);
|
|
CHECK(res->values().at(0).data().id() == 2);
|
|
CHECK(res->next_page());
|
|
CHECK(res->prev_page());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace database
|
|
|