1 /********************************************************************
2 Copyright 2018  David Edmundson <davidedmundson@kde.org>
3 
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11 
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 Lesser General Public License for more details.
16 
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library.  If not, see <http://www.gnu.org/licenses/>.
19 *********************************************************************/
20 #include <QtTest>
21 
22 #include "../../server/display.h"
23 #include "../../server/output.h"
24 
25 #include "../../server/xdg_output.h"
26 #include "../../src/client/connection_thread.h"
27 #include "../../src/client/event_queue.h"
28 #include "../../src/client/output.h"
29 #include "../../src/client/registry.h"
30 #include "../../src/client/xdgoutput.h"
31 
32 class TestXdgOutput : public QObject
33 {
34     Q_OBJECT
35 public:
36     explicit TestXdgOutput(QObject* parent = nullptr);
37 private Q_SLOTS:
38     void init();
39     void cleanup();
40 
41     void testChanges_data();
42     void testChanges();
43 
44 private:
45     Wrapland::Server::Display* m_display;
46     Wrapland::Server::Output* m_serverOutput;
47 
48     std::string m_name = "HDMI-A";
49     std::string m_make = "Foocorp";
50     std::string m_model = "Barmodel";
51     std::string m_description;
52 
53     Wrapland::Client::ConnectionThread* m_connection;
54     Wrapland::Client::EventQueue* m_queue;
55     QThread* m_thread;
56 };
57 
58 static const QString s_socketName = QStringLiteral("wrapland-test-xdg-output-0");
59 
TestXdgOutput(QObject * parent)60 TestXdgOutput::TestXdgOutput(QObject* parent)
61     : QObject(parent)
62     , m_display(nullptr)
63     , m_serverOutput(nullptr)
64     , m_connection(nullptr)
65     , m_thread(nullptr)
66 {
67 }
68 
init()69 void TestXdgOutput::init()
70 {
71     using namespace Wrapland::Server;
72     delete m_display;
73     m_display = new Display(this);
74     m_display->setSocketName(s_socketName);
75     m_display->start();
76 
77     m_serverOutput = new Output(m_display);
78     m_serverOutput->add_mode(Output::Mode{QSize(1920, 1080), 60000, true, 1});
79     m_serverOutput->set_mode(1);
80     m_serverOutput->set_enabled(true);
81 
82     m_serverOutput->set_name(m_name);
83     m_serverOutput->set_make(m_make);
84     m_serverOutput->set_model(m_model);
85 
86     m_serverOutput->generate_description();
87     m_description = m_serverOutput->description();
88 
89     // Not a sensible position for one monitor but works for this test. And a 1.5 scale factor.
90     m_serverOutput->set_geometry(QRectF(QPoint(11, 12), QSize(1280, 720)));
91     m_serverOutput->done();
92 
93     // setup connection
94     m_connection = new Wrapland::Client::ConnectionThread;
95     QSignalSpy connectedSpy(m_connection, &Wrapland::Client::ConnectionThread::establishedChanged);
96     m_connection->setSocketName(s_socketName);
97 
98     m_thread = new QThread(this);
99     m_connection->moveToThread(m_thread);
100     m_thread->start();
101 
102     m_connection->establishConnection();
103     QVERIFY(connectedSpy.count() || connectedSpy.wait());
104     QCOMPARE(connectedSpy.count(), 1);
105 
106     m_queue = new Wrapland::Client::EventQueue(this);
107     QVERIFY(!m_queue->isValid());
108     m_queue->setup(m_connection);
109     QVERIFY(m_queue->isValid());
110 }
111 
cleanup()112 void TestXdgOutput::cleanup()
113 {
114     if (m_queue) {
115         delete m_queue;
116         m_queue = nullptr;
117     }
118     if (m_thread) {
119         m_thread->quit();
120         m_thread->wait();
121         delete m_thread;
122         m_thread = nullptr;
123     }
124     delete m_connection;
125     m_connection = nullptr;
126 
127     delete m_serverOutput;
128     m_serverOutput = nullptr;
129 
130     delete m_display;
131     m_display = nullptr;
132 }
133 
testChanges_data()134 void TestXdgOutput::testChanges_data()
135 {
136     QTest::addColumn<int>("client_version");
137 
138     QTest::newRow("v1") << 1;
139     QTest::newRow("v2") << 2;
140     QTest::newRow("v3") << 3;
141 }
142 
testChanges()143 void TestXdgOutput::testChanges()
144 {
145     // Verify the server modes.
146     QFETCH(int, client_version);
147 
148     Wrapland::Client::Registry registry;
149     QSignalSpy announced(&registry, SIGNAL(outputAnnounced(quint32, quint32)));
150     QSignalSpy xdgOutputAnnounced(&registry, SIGNAL(xdgOutputAnnounced(quint32, quint32)));
151 
152     registry.setEventQueue(m_queue);
153     registry.create(m_connection->display());
154     QVERIFY(registry.isValid());
155     registry.setup();
156     QVERIFY(announced.wait());
157     if (xdgOutputAnnounced.count() != 1) {
158         QVERIFY(xdgOutputAnnounced.wait());
159     }
160 
161     Wrapland::Client::Output output;
162     QSignalSpy outputChanged(&output, SIGNAL(changed()));
163 
164     output.setup(registry.bindOutput(announced.first().first().value<quint32>(),
165                                      announced.first().last().value<quint32>()));
166     QVERIFY(outputChanged.wait());
167 
168     QVERIFY(xdgOutputAnnounced.first().last().value<quint32>()
169             >= static_cast<quint32>(client_version));
170 
171     std::unique_ptr<Wrapland::Client::XdgOutputManager> xdgOutputManager(
172         registry.createXdgOutputManager(
173             xdgOutputAnnounced.first().first().value<quint32>(), client_version, this));
174 
175     std::unique_ptr<Wrapland::Client::XdgOutput> xdgOutput(
176         xdgOutputManager->getXdgOutput(&output, this));
177     QSignalSpy xdgOutputChanged(xdgOutput.get(), SIGNAL(changed()));
178 
179     // check details are sent on client bind
180     QVERIFY(xdgOutputChanged.wait());
181     xdgOutputChanged.clear();
182 
183     QCOMPARE(xdgOutput->logicalPosition(), QPoint(11, 12));
184     QCOMPARE(xdgOutput->logicalSize(), QSize(1280, 720));
185 
186     if (client_version == 1) {
187         QCOMPARE(xdgOutput->name(), "");
188         QCOMPARE(xdgOutput->description(), "");
189     } else {
190         QCOMPARE(xdgOutput->name(), m_name);
191         QCOMPARE(xdgOutput->description(), m_description);
192     }
193 
194     // dynamic updates
195     m_serverOutput->set_geometry(QRectF(QPoint(1000, 2000), QSize(100, 200)));
196 
197     std::string updated_description = "Updated xdg-output description";
198     m_serverOutput->set_description(updated_description);
199     m_serverOutput->done();
200 
201     QVERIFY(xdgOutputChanged.wait());
202     QCOMPARE(xdgOutputChanged.count(), 1);
203 
204     QCOMPARE(xdgOutput->logicalPosition(), QPoint(1000, 2000));
205     QCOMPARE(xdgOutput->logicalSize(), QSize(100, 200));
206 
207     if (client_version == 1) {
208         QCOMPARE(xdgOutput->description(), "");
209     } else if (client_version == 2) {
210         // Description is immutable in version 2.
211         QCOMPARE(xdgOutput->description(), m_description);
212     } else {
213         QCOMPARE(xdgOutput->description(), updated_description);
214     }
215 }
216 
217 QTEST_GUILESS_MAIN(TestXdgOutput)
218 #include "xdg_output.moc"
219