File indexing completed on 2025-01-05 04:47:39

0001 /*
0002  * SPDX-FileCopyrightText: 2021 Glen Ditchfield <GJDitchfield@acm.org>
0003  * SPDX-License-Identifier: GPL-2.0-or-later
0004  */
0005 
0006 #include <QList>
0007 #include <QTest>
0008 
0009 #include "cellitem.h"
0010 
0011 class PlaceItemTest : public QObject
0012 {
0013     Q_OBJECT
0014 private Q_SLOTS:
0015     void soleItemHasNoOverlaps();
0016     void itemDoesNotOverlapItself();
0017     void twoItemsShareTheirCell();
0018     void variousOverlapPositions_data();
0019     void variousOverlapPositions();
0020     void fillLeftGap();
0021     void fillCenterGap();
0022     void transitiveOverlap();
0023 };
0024 
0025 using namespace CalendarSupport;
0026 
0027 // Skeletal item class, not unlike EventViews::AgendaItem.  Instances represent
0028 // a range of cell indexes from startAt up to but not including endBefore.
0029 struct TestItem : public CellItem {
0030     const QString name;
0031     const int startAt, endBefore;
0032 
0033     TestItem(const char *n, int s, int e)
0034         : name(QString::fromLatin1(n))
0035         , startAt(s)
0036         , endBefore(e)
0037     {
0038     }
0039 
0040     TestItem(const char *n, int s, int e, int subCell, int subCells)
0041         : name(QString::fromLatin1(n))
0042         , startAt(s)
0043         , endBefore(e)
0044     {
0045         setSubCell(subCell);
0046         setSubCells(subCells);
0047     }
0048 
0049     bool overlaps(CellItem *o) const override
0050     {
0051         auto other = static_cast<TestItem *>(o);
0052         return !(other->endBefore <= startAt || other->startAt >= endBefore);
0053     }
0054 
0055     [[nodiscard]] QString label() const override
0056     {
0057         return name;
0058     }
0059 };
0060 
0061 void PlaceItemTest::soleItemHasNoOverlaps()
0062 {
0063     auto item = std::make_unique<TestItem>("i", 1, 3);
0064     const QList<CellItem *> cells;
0065     auto overlappers = CellItem::placeItem(cells, item.get());
0066     QCOMPARE(overlappers.size(), 0);
0067     QCOMPARE(item->subCell(), 0);
0068     QCOMPARE(item->subCells(), 1);
0069 }
0070 
0071 void PlaceItemTest::itemDoesNotOverlapItself()
0072 {
0073     auto item = std::make_unique<TestItem>("i", 1, 3);
0074     const QList<CellItem *> cells({item.get()});
0075     auto overlappers = CellItem::placeItem(cells, item.get());
0076     QCOMPARE(overlappers.size(), 0);
0077     QCOMPARE(item->subCell(), 0);
0078     QCOMPARE(item->subCells(), 1);
0079 }
0080 
0081 void PlaceItemTest::twoItemsShareTheirCell()
0082 {
0083     auto oldItem = std::make_unique<TestItem>("i1", 1, 2, 0, 1);
0084     const QList<CellItem *> cells({oldItem.get()});
0085     auto newItem = std::make_unique<TestItem>("i2", 1, 2);
0086     auto overlappers = CellItem::placeItem(cells, newItem.get());
0087     QCOMPARE(overlappers.size(), 2);
0088     QCOMPARE(oldItem->subCells(), 2);
0089     QCOMPARE(newItem->subCells(), 2);
0090     QVERIFY(oldItem->subCell() != newItem->subCell());
0091     QVERIFY(oldItem->subCell() < oldItem->subCells());
0092     QVERIFY(newItem->subCell() < newItem->subCells());
0093 }
0094 
0095 void PlaceItemTest::variousOverlapPositions_data()
0096 {
0097     QTest::addColumn<int>("startAt");
0098     QTest::addColumn<bool>("shouldOverlap");
0099 
0100     QTest::newRow("before top") << 0 << false;
0101     QTest::newRow("overlaps top") << 1 << true;
0102     QTest::newRow("at top") << 2 << true;
0103     QTest::newRow("inside") << 3 << true;
0104     QTest::newRow("at bottom") << 4 << true;
0105     QTest::newRow("overlaps bottom") << 5 << true;
0106     QTest::newRow("after bottom") << 6 << false;
0107 }
0108 
0109 // Evaluate placement of a new item at various positions relative to an existing
0110 // item that covers many cells.
0111 void PlaceItemTest::variousOverlapPositions()
0112 {
0113     QFETCH(int, startAt);
0114     QFETCH(bool, shouldOverlap);
0115 
0116     auto oldItem = std::make_unique<TestItem>("old", 2, 6, 0, 1);
0117     const QList<CellItem *> cells({oldItem.get()});
0118     auto newItem = std::make_unique<TestItem>("new", startAt, startAt + 2);
0119     auto overlappers = CellItem::placeItem(cells, newItem.get());
0120     QCOMPARE(overlappers.size(), shouldOverlap ? 2 : 0);
0121     QVERIFY(!shouldOverlap || oldItem->subCell() != newItem->subCell());
0122     QCOMPARE(oldItem->subCells(), shouldOverlap ? 2 : 1);
0123     QCOMPARE(newItem->subCells(), shouldOverlap ? 2 : 1);
0124 }
0125 
0126 // |item1|              |item1|
0127 // |_____||item2|  -->  |_____||item2|
0128 //        |_____|       | new ||_____|
0129 //                      |_____|
0130 void PlaceItemTest::fillLeftGap()
0131 {
0132     auto item1 = std::make_unique<TestItem>("item1", 0, 2, 0, 2);
0133     auto item2 = std::make_unique<TestItem>("item2", 1, 3, 1, 2);
0134     const QList<CellItem *> cells({item1.get(), item2.get()});
0135     auto newItem = std::make_unique<TestItem>("new", 2, 4);
0136     (void)CellItem::placeItem(cells, newItem.get());
0137     QCOMPARE(newItem->subCell(), item1->subCell());
0138     QCOMPARE(newItem->subCells(), 2);
0139 }
0140 
0141 //        |item1|                     |item1|
0142 // |item2||_____||item3|  -->  |item2||_____||item3|
0143 // |_____|       |_____|       |_____|| new ||_____|
0144 //                                    |_____|
0145 void PlaceItemTest::fillCenterGap()
0146 {
0147     auto item1 = std::make_unique<TestItem>("item1", 0, 2, 1, 3);
0148     auto item2 = std::make_unique<TestItem>("item2", 1, 3, 0, 3);
0149     auto item3 = std::make_unique<TestItem>("item3", 1, 3, 2, 3);
0150     const QList<CellItem *> cells({item1.get(), item2.get(), item3.get()});
0151     auto newItem = std::make_unique<TestItem>("new", 2, 4);
0152     (void)CellItem::placeItem(cells, newItem.get());
0153     QCOMPARE(newItem->subCell(), item1->subCell());
0154     QCOMPARE(newItem->subCells(), 3);
0155 }
0156 
0157 // Items that do not overlap placeItem may also need adjustment.
0158 // See https://bugs.kde.org/show_bug.cgi?id=64603
0159 // |item1   |                 |item1|
0160 // |________||item3   |  -->  |_____||item3|
0161 // |item2   ||________|       |item2||_____|| new |
0162 // |________|                 |_____|       |_____|
0163 void PlaceItemTest::transitiveOverlap()
0164 {
0165     auto item1 = std::make_unique<TestItem>("item1", 0, 2, 0, 2);
0166     auto item2 = std::make_unique<TestItem>("item2", 2, 4, 0, 2);
0167     auto item3 = std::make_unique<TestItem>("item3", 1, 3, 1, 2);
0168     const QList<CellItem *> cells({item1.get(), item2.get(), item3.get()});
0169     auto newItem = std::make_unique<TestItem>("new", 2, 4);
0170     (void)CellItem::placeItem(cells, newItem.get());
0171     QCOMPARE(newItem->subCells(), 3);
0172     QCOMPARE(item1->subCells(), 3);
0173     QCOMPARE(item2->subCells(), 3);
0174     QCOMPARE(item3->subCells(), 3);
0175 }
0176 
0177 QTEST_MAIN(PlaceItemTest)
0178 
0179 #include "placeitemtest.moc"