File indexing completed on 2024-04-21 14:47:18

0001 /*
0002     SPDX-FileCopyrightText: 2021 Valentin Boettcher <hiro at protagon.space; @hiro98:tchncs.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include <QTest>
0008 #include <QtConcurrent/QtConcurrentRun>
0009 #include <QTemporaryDir>
0010 #include <QTemporaryFile>
0011 #include <qtestcase.h>
0012 #include "catalogsdb.h"
0013 #include "skymesh.h"
0014 
0015 using namespace CatalogsDB;
0016 class TestCatalogsDB_DBManager : public QObject
0017 {
0018     Q_OBJECT
0019   public:
0020     TestCatalogsDB_DBManager() : m_manager{ getTestSqliteFile() } {}
0021 
0022   private:
0023     const QString db_file_og = QFINDTESTDATA("data/test.sqlite");
0024     DBManager m_manager;
0025     CatalogObject some_object() { return m_manager.get_objects(99, 1).front(); }
0026     QString getTestSqliteFile()
0027     {
0028         const QString db_file = "test.sqlite";
0029         QFile::remove(db_file);
0030         QFile::copy(db_file_og, db_file);
0031         return db_file;
0032     }
0033 
0034   private slots:
0035     void init()
0036     {
0037         // A fresh manager for each test.
0038         m_manager = DBManager{ getTestSqliteFile() };
0039     };
0040 
0041     void user_catalog_created()
0042     {
0043         QTemporaryFile tmp;
0044         m_manager = DBManager{ tmp.fileName() };
0045         QCOMPARE(m_manager.get_catalog(0).second.id, 0);
0046     }
0047 
0048     void getting_catalogs()
0049     {
0050         // first without disabled
0051         const auto &cats = m_manager.get_catalogs();
0052         for (const auto &cat : cats)
0053         {
0054             // the catalogs should be the same
0055             QCOMPARE(m_manager.get_catalog(cat.id).second.id, cat.id);
0056             // and they should exist
0057             QVERIFY(m_manager.catalog_exists(cat.id));
0058             QVERIFY(cat.enabled);
0059         }
0060 
0061         // and then with disabled
0062         const auto &cats_d = m_manager.get_catalogs(true);
0063         for (const auto &cat : cats_d)
0064         {
0065             // the catalogs should be the same
0066             QCOMPARE(m_manager.get_catalog(cat.id).second.id, cat.id);
0067             // and they should exist
0068             QVERIFY(m_manager.catalog_exists(cat.id));
0069         }
0070     }
0071 
0072     void getting_objects_in_trixel()
0073     {
0074         const int num_trixels = SkyMesh::Create(m_manager.htmesh_level())->size();
0075         int num_obj           = 0;
0076 
0077         QBENCHMARK
0078         {
0079             for (int trixel = 0; trixel < num_trixels; trixel++)
0080             {
0081                 num_obj += m_manager.get_objects_in_trixel(trixel).size();
0082             }
0083         }
0084 
0085         QVERIFY(num_obj > 0);
0086     }
0087 
0088     void find_by_name()
0089     {
0090         const auto &obj  = some_object();
0091         const auto &objs = m_manager.find_objects_by_name(obj.name(), 1);
0092         QCOMPARE(objs.size(), 1);
0093         QCOMPARE(obj.name(), objs.front().name());
0094     }
0095 
0096     void get_by_id()
0097     {
0098         const auto &obj     = some_object();
0099         const auto &fetched = m_manager.get_object(obj.getObjectId());
0100 
0101         QVERIFY(fetched.first);
0102         QCOMPARE(fetched.second, obj);
0103 
0104         const auto &fetched_in_cat =
0105             m_manager.get_object(obj.getObjectId(), obj.getCatalog().id);
0106 
0107         QVERIFY(fetched_in_cat.first);
0108         QCOMPARE(fetched_in_cat.second, obj);
0109     }
0110 
0111     void get_objects_by_maglim()
0112     {
0113         const auto &objs = m_manager.get_objects(1, 10);
0114         QVERIFY(objs.size() <= 10);
0115 
0116         for (const auto &obj : objs)
0117         {
0118             QVERIFY(obj.mag() <= 1);
0119         }
0120     }
0121 
0122     void get_objects_by_type()
0123     {
0124         const auto &objs = m_manager.get_objects(SkyObject::SUPERNOVA_REMNANT, 1, 10);
0125         QVERIFY(objs.size() <= 10);
0126 
0127         for (const auto &obj : objs)
0128         {
0129             QVERIFY(obj.mag() <= 1);
0130             QCOMPARE(obj.type(), SkyObject::SUPERNOVA_REMNANT);
0131         }
0132     }
0133 
0134     void get_objects_by_type_in_catalog()
0135     {
0136         for (const auto &cat : m_manager.get_catalogs(true))
0137         {
0138             const auto &cat_id = cat.id;
0139             const auto &objs   = m_manager.get_objects_in_catalog(
0140                 SkyObject::SUPERNOVA_REMNANT, cat_id, 1, 10);
0141             QVERIFY(objs.size() <= 10);
0142 
0143             for (const auto &obj : objs)
0144             {
0145                 QVERIFY(obj.mag() <= 1);
0146                 QCOMPARE(obj.type(), SkyObject::SUPERNOVA_REMNANT);
0147                 QCOMPARE(obj.getCatalog().id, cat_id);
0148             }
0149         }
0150     }
0151 
0152     void enable_disable_catalogs()
0153     {
0154         for (const auto &cat : m_manager.get_catalogs(true))
0155         {
0156             for (const auto enable : { true, false })
0157             {
0158                 const auto &success = m_manager.set_catalog_enabled(cat.id, enable);
0159                 QVERIFY(success.first);
0160                 QCOMPARE(m_manager.get_catalog(cat.id).second.enabled, enable);
0161             }
0162         }
0163 
0164         const auto &objs = m_manager.get_objects();
0165         QCOMPARE(objs.size(), 0);
0166     }
0167 
0168     void remove_catalog()
0169     {
0170         for (const auto &cat : m_manager.get_catalogs(true))
0171         {
0172             const auto size     = m_manager.get_catalogs(true).size();
0173             const auto &success = m_manager.remove_catalog(cat.id);
0174             if (cat.id == 0) // user catalog cannot be deleted
0175             {
0176                 QVERIFY(!success.first);
0177                 continue;
0178             }
0179 
0180             QVERIFY(success.first);
0181             QCOMPARE(m_manager.get_catalogs(true).size(), size - 1);
0182         }
0183     }
0184 
0185     void register_dump_and_read_catalog()
0186     {
0187         QTemporaryDir dir;
0188         QVERIFY(dir.isValid());
0189         const auto &path = QString("%1/%2").arg(dir.path()).arg("tmp.sql");
0190 
0191         const auto &names = { "a", "b", "c" };
0192         const int id      = m_manager.find_suitable_catalog_id();
0193 
0194         const auto &now = QDateTime::currentDateTime();
0195         auto success =
0196             m_manager.register_catalog(id, "test", true, true, 1, "me", "test suite",
0197                                        "test catalog", 2, "green", "gpl", "me", now);
0198         QVERIFY(success.first);
0199 
0200         for (const auto &name : names)
0201             m_manager.add_object(id, SkyObject::STAR, dms{ 0 }, dms{ 0 }, name);
0202 
0203         const auto &objects = m_manager.get_objects_in_catalog(SkyObject::STAR, id);
0204 
0205         success = m_manager.dump_catalog(id, path);
0206         QVERIFY2(success.first, "Dumping catalogs.");
0207 
0208         const auto cat_meta = CatalogsDB::read_catalog_meta_from_file(path);
0209 
0210         QVERIFY2(cat_meta.first, "Read dumped catalog meta data.");
0211         QCOMPARE(cat_meta.second.id, id);
0212         QCOMPARE(cat_meta.second.name, "test");
0213         QCOMPARE(cat_meta.second.precedence, 1);
0214         QCOMPARE(cat_meta.second.author, "me");
0215         QCOMPARE(cat_meta.second.mut, true);
0216         QCOMPARE(cat_meta.second.enabled, true);
0217         QCOMPARE(cat_meta.second.source, "test suite");
0218         QCOMPARE(cat_meta.second.description, "test catalog");
0219         QCOMPARE(cat_meta.second.version, 2);
0220         QCOMPARE(cat_meta.second.color, "green");
0221         QCOMPARE(cat_meta.second.license, "gpl");
0222         QCOMPARE(cat_meta.second.maintainer, "me");
0223         QCOMPARE(cat_meta.second.timestamp, now);
0224 
0225         success = m_manager.import_catalog(path);
0226         QVERIFY2(!success.first, "Not overwriting an existing catalog.");
0227 
0228         success = m_manager.import_catalog(path, true);
0229         QVERIFY2(success.first, "Importing and overwriting."); // import and overwrite
0230 
0231         success = m_manager.remove_catalog(id); // remove and import fresh
0232         QVERIFY(!m_manager.get_catalog(id).first);
0233 
0234         success = m_manager.import_catalog(path);
0235         QVERIFY2(success.first, "Importing fresh.");
0236 
0237         const auto &new_objects = m_manager.get_objects_in_catalog(SkyObject::STAR, id);
0238         for (const auto &object : objects)
0239         {
0240             QVERIFY2(std::find(new_objects.cbegin(), new_objects.cend(), object) !=
0241                          new_objects.cend(),
0242                      "Object found in reimported catalog.");
0243         }
0244     }
0245 
0246     void edit_catalog()
0247     {
0248         const Catalog cat = {
0249             0, "New User", 0, "test", "toast", "toaster", false, false
0250         };
0251 
0252         auto success = m_manager.update_catalog_meta(cat);
0253         QVERIFY2(success.first, "Changing meta works.");
0254         QCOMPARE(m_manager.get_catalog(0).second.name, "New User");
0255         QCOMPARE(m_manager.get_catalog(0).second.author, "test");
0256         QCOMPARE(m_manager.get_catalog(0).second.source, "toast");
0257         QCOMPARE(m_manager.get_catalog(0).second.description, "toaster");
0258         QCOMPARE(m_manager.get_catalog(0).second.mut, true); // did not change
0259 
0260         success = m_manager.update_catalog_meta({ -1, "bla" });
0261         QVERIFY2(!success.first, "Changing meta of nonexisting catalog doesn't work.");
0262     }
0263 
0264     void register_from_catalog_objetct()
0265     {
0266         const Catalog cat{ m_manager.find_suitable_catalog_id(),
0267                            "test",
0268                            1,
0269                            "tester",
0270                            "test catalog",
0271                            "testing catalog",
0272                            true,
0273                            false,
0274                            100 };
0275 
0276         auto success = m_manager.register_catalog(cat);
0277         QVERIFY2(success.first, "Registering a catalog worked.");
0278 
0279         const auto &success_fetch = m_manager.get_catalog(cat.id);
0280         QVERIFY2(success_fetch.first, "Inserted catalog found.");
0281 
0282         const auto &cat_new = success_fetch.second;
0283         QCOMPARE(cat_new.id, cat.id);
0284         QCOMPARE(cat_new.name, cat.name);
0285         QCOMPARE(cat_new.precedence, cat.precedence);
0286         QCOMPARE(cat_new.author, cat.author);
0287         QCOMPARE(cat_new.source, cat.source);
0288         QCOMPARE(cat_new.description, cat.description);
0289         QCOMPARE(cat_new.enabled, cat.enabled);
0290         QCOMPARE(cat_new.version, cat.version);
0291     }
0292 
0293     void add_object_and_copy()
0294     {
0295         const Catalog cat{ m_manager.find_suitable_catalog_id(),
0296                            "test",
0297                            1,
0298                            "tester",
0299                            "test catalog",
0300                            "testing catalog",
0301                            true,
0302                            false,
0303                            100 };
0304 
0305         auto success = m_manager.register_catalog(cat);
0306         QVERIFY2(success.first, "Registering a catalog worked.");
0307 
0308         auto success_add =
0309             m_manager.add_object(cat.id, SkyObject::STAR, dms{ 1 }, dms{ 0 }, "test", -1,
0310                                  "long test", "NGC 100", 10, 11, 111, 0);
0311         QVERIFY2(success_add.first, "Inserting an object worked.");
0312 
0313         const auto obj =
0314             m_manager.get_objects_in_catalog(SkyObject::STAR, cat.id, 99, 1).front();
0315 
0316         QCOMPARE(obj.type(), SkyObject::STAR);
0317         QCOMPARE(obj.ra0(), dms{ 1 });
0318         QCOMPARE(obj.dec0(), dms{ 0 });
0319         QCOMPARE(obj.name(), "test");
0320         QCOMPARE(obj.mag(), -1);
0321         QCOMPARE(obj.longname(), "long test");
0322         QCOMPARE(obj.catalogIdentifier(), "NGC 100");
0323         QCOMPARE(obj.a(), 10);
0324         QCOMPARE(obj.b(), 11);
0325         QCOMPARE(obj.pa(), 111);
0326         QCOMPARE(obj.flux(), 0);
0327 
0328         success_add = m_manager.add_object(cat.id, obj);
0329         QVERIFY2(success_add.first, "Can add the same object twice = update!");
0330         QCOMPARE(m_manager.get_objects_in_catalog(SkyObject::STAR, cat.id, 99, 1).size(),
0331                  1);
0332 
0333         const CatalogObject obj_direct{
0334             {},          SkyObject::STAR, dms{ 1 }, dms{ 11 }, -1,  "test_1",
0335             "long test", "NGC 100",       10,       11,        111, 0
0336         };
0337 
0338         success_add = m_manager.add_object(cat.id, obj_direct);
0339         QVERIFY2(success_add.first, "Can add predefined catalog object.");
0340 
0341         const auto obj_retr = m_manager.find_objects_by_name(cat.id, "test_1", 1).front();
0342         QCOMPARE(obj_direct.type(), obj_retr.type());
0343         QCOMPARE(obj_direct.ra0(), obj_retr.ra0());
0344         QCOMPARE(obj_direct.dec0(), obj_retr.dec0());
0345         QCOMPARE(obj_direct.name(), obj_retr.name());
0346         QCOMPARE(obj_direct.mag(), obj_retr.mag());
0347         QCOMPARE(obj_direct.longname(), obj_retr.longname());
0348         QCOMPARE(obj_direct.catalogIdentifier(), obj_retr.catalogIdentifier());
0349         QCOMPARE(obj_direct.a(), obj_retr.a());
0350         QCOMPARE(obj_direct.b(), obj_retr.b());
0351         QCOMPARE(obj_direct.pa(), obj_retr.pa());
0352         QCOMPARE(obj_direct.flux(), obj_retr.flux());
0353 
0354         const auto &success_copy = m_manager.copy_objects(cat.id, 0);
0355         QVERIFY2(success_copy.first, "Can copy objects.");
0356 
0357         QCOMPARE(m_manager.find_objects_by_name(0, "test", 1).size(), 1);
0358         QCOMPARE(m_manager.find_objects_by_name(0, "test", 1).front().getCatalog().id, 0);
0359 
0360         QCOMPARE(m_manager.find_objects_by_name(0, "test_1", 1).size(), 1);
0361         QCOMPARE(m_manager.get_objects_in_catalog(SkyObject::STAR, 0).size(), 2);
0362     }
0363 
0364     void add_and_remove_object()
0365     {
0366         auto success =
0367             m_manager.add_object(0, SkyObject::STAR, dms{ 0 }, dms{ 0 }, "tester");
0368         QVERIFY(success.first);
0369         QCOMPARE(m_manager.find_objects_by_name(0, "tester", 1).front().name(), "tester");
0370 
0371         const CatalogObject o{ {}, SkyObject::STAR, dms{ 0 }, dms{ 0 }, 0, "tester_1" };
0372         success = m_manager.add_object(0, o);
0373         QVERIFY(success.first);
0374         QCOMPARE(m_manager.find_objects_by_name(0, "tester_1", 1).front().getId(), o);
0375 
0376         success = m_manager.remove_object(0, o.getObjectId());
0377         QVERIFY(success.first);
0378         QCOMPARE(m_manager.find_objects_by_name(0, "tester_1", 1).size(), 0);
0379     }
0380 
0381     void add_objects()
0382     {
0383         const Catalog cat{ m_manager.find_suitable_catalog_id(),
0384                            "test",
0385                            1,
0386                            "tester",
0387                            "test catalog",
0388                            "testing catalog",
0389                            true,
0390                            false,
0391                            100 };
0392 
0393         auto success = m_manager.register_catalog(cat);
0394         QVERIFY2(success.first, "Registering a catalog worked.");
0395 
0396         const int num_objs{ 1000 };
0397         std::vector<float> test_mags(num_objs);
0398         std::iota(test_mags.begin(), test_mags.end(), 1);
0399         std::vector<CatalogObject> objects;
0400         std::transform(
0401             test_mags.begin(), test_mags.end(), std::back_inserter(objects),
0402             [](const auto mag) {
0403                 return CatalogObject{ {},          SkyObject::STAR,
0404                                       dms{ 1 },    dms{ 11 },
0405                                       -1,          QString("test_%1_").arg(mag),
0406                                       "long test", "NGC 100",
0407                                       10,          11,
0408                                       111,         0 };
0409             });
0410 
0411         auto success_add = m_manager.add_objects(cat.id, objects);
0412         QVERIFY2(success_add.first, "Inserting an object worked.");
0413         QCOMPARE(m_manager.get_catalog_statistics(cat.id).second.total_count, num_objs);
0414 
0415         for (const auto &obj : objects)
0416         {
0417             const auto obj_retr =
0418                 m_manager.find_objects_by_name(cat.id, obj.name(), 1).front();
0419             QCOMPARE(obj.type(), obj_retr.type());
0420             QCOMPARE(obj.ra0(), obj_retr.ra0());
0421             QCOMPARE(obj.dec0(), obj_retr.dec0());
0422             QCOMPARE(obj.name(), obj_retr.name());
0423             QCOMPARE(obj.mag(), obj_retr.mag());
0424             QCOMPARE(obj.longname(), obj_retr.longname());
0425             QCOMPARE(obj.catalogIdentifier(), obj_retr.catalogIdentifier());
0426             QCOMPARE(obj.a(), obj_retr.a());
0427             QCOMPARE(obj.b(), obj_retr.b());
0428             QCOMPARE(obj.pa(), obj_retr.pa());
0429             QCOMPARE(obj.flux(), obj_retr.flux());
0430         }
0431     }
0432 
0433     void concurrent_query()
0434     {
0435         auto f1 = QtConcurrent::run([&] {
0436             const auto &objs = m_manager.get_objects();
0437             return objs.size() > 0;
0438         });
0439 
0440         auto f2 = QtConcurrent::run([&] {
0441             const auto &objs = m_manager.get_objects();
0442             return objs.size() > 0;
0443         });
0444 
0445         f1.waitForFinished();
0446         f2.waitForFinished();
0447 
0448         QVERIFY(f1.result() && f2.result());
0449     }
0450 
0451     void statistics()
0452     {
0453         auto success_add =
0454             m_manager.add_object(user_catalog_id, SkyObject::STAR, dms{ 1 }, dms{ 0 },
0455                                  "test", -1, "long test", "NGC 100", 10, 11, 111, 0);
0456         QVERIFY2(success_add.first, "Adding object succeeds.");
0457 
0458         success_add = m_manager.add_object(user_catalog_id, SkyObject::SUPERNOVA_REMNANT,
0459                                            dms{ 1 }, dms{ 0 }, "test", -1, "long test",
0460                                            "NGC 100", 10, 11, 111, 0);
0461         QVERIFY2(success_add.first, "Adding object succeeds.");
0462 
0463         auto stats = m_manager.get_catalog_statistics(0);
0464         QVERIFY2(stats.first, "retrieving stats works");
0465         QCOMPARE(stats.second.total_count, 2);
0466         QCOMPARE(stats.second.object_counts.at(SkyObject::STAR), 1);
0467         QCOMPARE(stats.second.object_counts.at(SkyObject::SUPERNOVA_REMNANT), 1);
0468 
0469         stats = m_manager.get_master_statistics();
0470         QVERIFY2(stats.first, "retrieving stats works");
0471         QVERIFY(stats.second.total_count > 0);
0472         QVERIFY(stats.second.object_counts.at(SkyObject::STAR) >= 1);
0473         QVERIFY(stats.second.object_counts.at(SkyObject::SUPERNOVA_REMNANT) >= 1);
0474     }
0475 
0476     void color_strings()
0477     {
0478         const std::vector<std::pair<QString, CatalogsDB::CatalogColorMap>> test_data{
0479             { "#008000", { { "default", "#008000" } } },
0480             { "#008000;test.colors;#008001",
0481               { { "default", "#008000" }, { "test.colors", "#008001" } } },
0482             { "#008000;test.colors;#008001;best.colors;#008002",
0483               { { "default", "#008000" },
0484                 { "test.colors", "#008001" },
0485                 { "best.colors", "#008002" } } }
0486         };
0487 
0488         for (const auto &item : test_data)
0489         {
0490             QCOMPARE(CatalogsDB::parse_color_string(item.first), item.second);
0491 
0492             QCOMPARE(
0493                 CatalogsDB::parse_color_string(CatalogsDB::to_color_string(item.second)),
0494                 item.second); // the other way around does not have to be invertible
0495         }
0496 
0497         // more than one theme changes the order in the string
0498         const auto &simple = test_data.at(1);
0499         QCOMPARE(CatalogsDB::to_color_string(simple.second), simple.first);
0500 
0501         // Check the behavour when a color specification is missing
0502         QCOMPARE((CatalogsDB::parse_color_string("#008000;test.colors")),
0503                  (CatalogsDB::CatalogColorMap{ { "default", "#008000" } }));
0504 
0505         // no default
0506         QCOMPARE(CatalogsDB::parse_color_string(""), CatalogsDB::CatalogColorMap{});
0507         QCOMPARE(CatalogsDB::parse_color_string(";test;#008000"),
0508                  (CatalogsDB::CatalogColorMap{ { "test", "#008000" } }));
0509     }
0510 
0511     void color_database()
0512     {
0513         auto compare_catalog_color_maps = [](CatalogsDB::CatalogColorMap a,
0514                                              CatalogsDB::CatalogColorMap b) -> void {
0515             for (auto &item : a)
0516             {
0517                 QCOMPARE(b[item.first].name(), item.second.name());
0518             }
0519 
0520             for (auto &item : b)
0521             {
0522                 QCOMPARE(a[item.first].name(), item.second.name());
0523             }
0524         };
0525 
0526         auto compare_color_maps =
0527             [compare_catalog_color_maps](CatalogsDB::ColorMap a,
0528                                          CatalogsDB::ColorMap b) -> void {
0529             for (auto &item : a)
0530             {
0531                 compare_catalog_color_maps(b[item.first], item.second);
0532             }
0533 
0534             for (auto &item : b)
0535             {
0536                 compare_catalog_color_maps(a[item.first], item.second);
0537             }
0538         };
0539 
0540         const auto &colors = m_manager.get_catalog_colors();
0541         compare_color_maps((CatalogsDB::ColorMap{
0542                                { 1,
0543                                  {
0544                                      { "default", "#001000" },
0545                                      { "test.colors", "#008000" },
0546                                      { "test1.colors", "#008001" }, // overridden
0547                                      { "test2.colors", "#001002" }, // from catalog table
0548                                  } },
0549                                { 2,
0550                                  {
0551                                      { "test1.colors", "#008002" },
0552                                  } } }),
0553                            colors);
0554 
0555         compare_catalog_color_maps(m_manager.get_catalog_colors(1), colors.at(1));
0556 
0557         // overwrite default
0558         auto new_colors            = colors.at(1);
0559         new_colors["test2.colors"] = "#001003";
0560 
0561         const auto &success = m_manager.insert_catalog_colors(1, new_colors);
0562         QVERIFY(success.first);
0563 
0564         QCOMPARE(m_manager.get_catalog_colors(1).at("test2.colors"), "#001003");
0565 
0566         auto cat = m_manager.get_catalog(1);
0567         QVERIFY(cat.first);
0568         cat.second.color = "#001004";
0569         QVERIFY(m_manager.update_catalog_meta(cat.second).first);
0570         QCOMPARE(m_manager.get_catalog_colors(1).at("default"),
0571                  "#001000"); // does not change
0572     }
0573 };
0574 
0575 QTEST_GUILESS_MAIN(TestCatalogsDB_DBManager);