fixes: #9725 BREAKING_CHANGE: log level values have changed settings for log level may need to be reset
377 lines
11 KiB
C++
377 lines
11 KiB
C++
/*
|
|
* Deskflow -- mouse and keyboard sharing utility
|
|
* SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
|
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
|
*/
|
|
|
|
#include "WlClipboardTests.h"
|
|
|
|
#include "base/LogLevel.h"
|
|
#include "common/PlatformInfo.h"
|
|
#include "deskflow/ClipboardTypes.h"
|
|
#include "platform/WlClipboard.h"
|
|
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <thread>
|
|
|
|
#include <QTest>
|
|
|
|
void WlClipboardTests::defaultCtor()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QCOMPARE(kClipboardClipboard, clipboard.getID());
|
|
|
|
WlClipboard primaryClipboard(kClipboardSelection);
|
|
QCOMPARE(kClipboardSelection, primaryClipboard.getID());
|
|
}
|
|
|
|
void WlClipboardTests::isAvailable()
|
|
{
|
|
// This test may fail on systems without wl-clipboard installed
|
|
// In CI environments, we might need to skip this test or mock the commands
|
|
bool available = WlClipboard::isAvailable();
|
|
|
|
// We don't assert true/false here because it depends on system setup
|
|
// Just verify the method doesn't crash and returns a boolean
|
|
QVERIFY(available == true || available == false);
|
|
}
|
|
|
|
void WlClipboardTests::initTestCase()
|
|
{
|
|
m_arch.init();
|
|
m_log.setFilter(LogLevel::Verbose);
|
|
|
|
// Only run tests if Wayland clipboard tools are available
|
|
if (!deskflow::platform::isWayland()) {
|
|
QSKIP("not running on wayland skipping Wayland clipboard tests");
|
|
}
|
|
|
|
// Only run tests if Wayland clipboard tools are available
|
|
if (!WlClipboard::isAvailable()) {
|
|
QSKIP("wl-clipboard tools not available, skipping Wayland clipboard tests");
|
|
}
|
|
|
|
// Test if Wayland commands actually work (not just exist)
|
|
WlClipboard testClipboard(kClipboardClipboard);
|
|
if (!testClipboard.open(0)) {
|
|
QSKIP("Failed to open Wayland clipboard, likely no Wayland session");
|
|
}
|
|
|
|
// Try to clear clipboard - if this fails, we're probably not in a Wayland environment
|
|
if (!testClipboard.empty()) {
|
|
testClipboard.close();
|
|
QSKIP("Wayland clipboard operations not working, skipping tests");
|
|
}
|
|
|
|
testClipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::cleanupTestCase()
|
|
{
|
|
// Clean up by emptying clipboards
|
|
try {
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
if (clipboard.open(0)) {
|
|
clipboard.empty();
|
|
clipboard.close();
|
|
}
|
|
} catch (...) {
|
|
// Ignore cleanup errors
|
|
}
|
|
}
|
|
|
|
void WlClipboardTests::open()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QVERIFY(clipboard.open(0));
|
|
|
|
// Opening again should return false
|
|
QVERIFY(!clipboard.open(1));
|
|
|
|
clipboard.close();
|
|
|
|
// Should be able to open again after closing
|
|
QVERIFY(clipboard.open(2));
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::empty()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QVERIFY(clipboard.open(0));
|
|
|
|
// First add some known content
|
|
clipboard.add(IClipboard::Format::Text, m_testString);
|
|
QVERIFY(clipboard.has(IClipboard::Format::Text));
|
|
QCOMPARE(clipboard.get(IClipboard::Format::Text), m_testString);
|
|
|
|
// Now empty the clipboard
|
|
QVERIFY(clipboard.empty());
|
|
|
|
// Wait for the empty operation to complete and verify our content is gone
|
|
// Use longer timeout for clipboard operations
|
|
QVERIFY(waitForClipboardEmpty(clipboard, m_testString, 3000));
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::singleFormat()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QVERIFY(clipboard.open(0));
|
|
|
|
// Clear clipboard first - if this fails, skip test
|
|
if (!clipboard.empty()) {
|
|
clipboard.close();
|
|
QSKIP("Wayland clipboard operations not working in test environment");
|
|
}
|
|
|
|
// Add text data
|
|
clipboard.add(IClipboard::Format::Text, m_testString);
|
|
|
|
// Wait for the data to be available
|
|
if (!waitForClipboardContent(clipboard, IClipboard::Format::Text, m_testString)) {
|
|
clipboard.close();
|
|
QSKIP("Wl-clipboard did not receive contents in time");
|
|
}
|
|
|
|
// Add different text data - this should replace the previous data
|
|
if (!clipboard.empty()) {
|
|
clipboard.close();
|
|
QSKIP("Wayland clipboard clear operations not working in test environment");
|
|
}
|
|
|
|
// Skip the wait for empty since we know clipboard operations aren't fully functional
|
|
clipboard.add(IClipboard::Format::Text, m_testString2);
|
|
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::multipleFormats()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QVERIFY(clipboard.open(0));
|
|
|
|
// Clear clipboard first
|
|
QVERIFY(clipboard.empty());
|
|
|
|
// Add text data first
|
|
clipboard.add(IClipboard::Format::Text, m_testString);
|
|
|
|
// Note: wl-clipboard typically handles one format at a time
|
|
// So we test formats separately rather than simultaneously
|
|
if (!waitForClipboardContent(clipboard, IClipboard::Format::Text, m_testString)) {
|
|
clipboard.close();
|
|
QSKIP("Wl-clipboard did not receive contents in time");
|
|
}
|
|
|
|
// HTML format is currently not supported in WlClipboard implementation
|
|
// So we skip HTML testing and just verify text format works
|
|
|
|
// Clear and add different text data to test format replacement
|
|
QVERIFY(clipboard.empty());
|
|
QVERIFY(waitForClipboardEmpty(clipboard, m_testString));
|
|
|
|
clipboard.add(IClipboard::Format::Text, m_testString2);
|
|
if (!waitForClipboardContent(clipboard, IClipboard::Format::Text, m_testString2)) {
|
|
clipboard.close();
|
|
QSKIP("Wl-clipboard did not receive contents in time");
|
|
}
|
|
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::hasFormat()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QVERIFY(clipboard.open(0));
|
|
|
|
// Clear clipboard first
|
|
QVERIFY(clipboard.empty());
|
|
|
|
// Wait for empty to take effect with longer timeout
|
|
QVERIFY(waitForClipboardEmpty(clipboard, "any", 3000));
|
|
|
|
// Initially should not have any formats
|
|
QVERIFY(!clipboard.has(IClipboard::Format::Text));
|
|
QVERIFY(!clipboard.has(IClipboard::Format::HTML));
|
|
QVERIFY(!clipboard.has(IClipboard::Format::Bitmap));
|
|
|
|
// Add text and verify
|
|
clipboard.add(IClipboard::Format::Text, m_testString);
|
|
if (!waitForClipboardContent(clipboard, IClipboard::Format::Text, m_testString)) {
|
|
clipboard.close();
|
|
QSKIP("Wl-clipboard did not receive contents in time");
|
|
}
|
|
QVERIFY(clipboard.has(IClipboard::Format::Text));
|
|
QVERIFY(!clipboard.has(IClipboard::Format::HTML)); // HTML not supported
|
|
QVERIFY(!clipboard.has(IClipboard::Format::Bitmap));
|
|
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::getTime()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QVERIFY(clipboard.open(100));
|
|
|
|
// Should return the time passed to open()
|
|
QCOMPARE(clipboard.getTime(), static_cast<IClipboard::Time>(100));
|
|
|
|
clipboard.close();
|
|
QVERIFY(clipboard.open(200));
|
|
QCOMPARE(clipboard.getTime(), static_cast<IClipboard::Time>(200));
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::monitoring()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
QVERIFY(clipboard.open(0));
|
|
|
|
// Clear clipboard first
|
|
QVERIFY(clipboard.empty());
|
|
|
|
// Start monitoring
|
|
clipboard.startMonitoring();
|
|
|
|
// Initially should not have changed
|
|
QVERIFY(!clipboard.hasChanged());
|
|
|
|
// Make a change to the clipboard using a separate clipboard instance
|
|
// to simulate external changes
|
|
WlClipboard externalClipboard(kClipboardClipboard);
|
|
if (externalClipboard.open(1)) {
|
|
externalClipboard.empty();
|
|
externalClipboard.add(IClipboard::Format::Text, m_testString);
|
|
externalClipboard.close();
|
|
}
|
|
|
|
// Wait for monitoring thread to detect change
|
|
auto changeDetected = [&clipboard]() { return clipboard.hasChanged(); };
|
|
waitForClipboardCondition(clipboard, changeDetected, 1000);
|
|
|
|
// Stop monitoring
|
|
clipboard.stopMonitoring();
|
|
clipboard.close();
|
|
|
|
// Note: Change detection might not work reliably in all test environments
|
|
// This test mainly verifies that monitoring doesn't crash
|
|
}
|
|
|
|
void WlClipboardTests::primaryClipboard()
|
|
{
|
|
// Test that primary clipboard works independently from regular clipboard
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
WlClipboard primaryClipboard(kClipboardSelection);
|
|
|
|
QVERIFY(clipboard.open(0));
|
|
QVERIFY(primaryClipboard.open(1));
|
|
|
|
// Clear both clipboards
|
|
QVERIFY(clipboard.empty());
|
|
QVERIFY(primaryClipboard.empty());
|
|
|
|
// Add different data to each
|
|
clipboard.add(IClipboard::Format::Text, m_testString);
|
|
primaryClipboard.add(IClipboard::Format::Text, m_testString2);
|
|
|
|
if (!waitForClipboardContent(clipboard, IClipboard::Format::Text, m_testString) ||
|
|
!waitForClipboardContent(primaryClipboard, IClipboard::Format::Text, m_testString2)) {
|
|
clipboard.close();
|
|
primaryClipboard.close();
|
|
QSKIP("Wl-clipboard did not receive contents in time");
|
|
}
|
|
|
|
// Make sure they are different
|
|
QCOMPARE_NE(clipboard.get(IClipboard::Format::Text), primaryClipboard.get(IClipboard::Format::Text));
|
|
|
|
clipboard.close();
|
|
primaryClipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::closeWithoutOpen()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
|
|
// Should be safe to call close without open
|
|
clipboard.close();
|
|
|
|
// After close, open should work
|
|
QVERIFY(clipboard.open(0));
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::addWithoutOpen()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
|
|
// Should not crash when adding without open
|
|
clipboard.add(IClipboard::Format::Text, m_testString);
|
|
|
|
// Should still be able to open and use normally
|
|
QVERIFY(clipboard.open(0));
|
|
clipboard.close();
|
|
}
|
|
|
|
void WlClipboardTests::getWithoutOpen()
|
|
{
|
|
WlClipboard clipboard(kClipboardClipboard);
|
|
|
|
// Should return empty string when getting without open
|
|
std::string result = clipboard.get(IClipboard::Format::Text);
|
|
QVERIFY(result.empty());
|
|
|
|
// Should return false for has() without open
|
|
QVERIFY(!clipboard.has(IClipboard::Format::Text));
|
|
|
|
// Should still be able to open and use normally
|
|
QVERIFY(clipboard.open(0));
|
|
clipboard.close();
|
|
}
|
|
|
|
bool WlClipboardTests::waitForClipboardCondition(WlClipboard &, std::function<bool()> condition, int timeoutMs)
|
|
{
|
|
auto startTime = std::chrono::steady_clock::now();
|
|
auto timeout = std::chrono::milliseconds(timeoutMs);
|
|
|
|
while (std::chrono::steady_clock::now() - startTime < timeout) {
|
|
if (condition()) {
|
|
return true;
|
|
}
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool WlClipboardTests::waitForClipboardEmpty(WlClipboard &clipboard, const std::string &previousContent, int timeoutMs)
|
|
{
|
|
auto condition = [&clipboard, &previousContent]() {
|
|
// Check if clipboard is empty or no longer contains our previous content
|
|
if (!clipboard.has(IClipboard::Format::Text)) {
|
|
return true;
|
|
}
|
|
std::string currentContent = clipboard.get(IClipboard::Format::Text);
|
|
// Consider it empty if content is empty or significantly different
|
|
return currentContent.empty() || currentContent != previousContent;
|
|
};
|
|
|
|
return waitForClipboardCondition(clipboard, condition, timeoutMs);
|
|
}
|
|
|
|
bool WlClipboardTests::waitForClipboardContent(
|
|
WlClipboard &clipboard, IClipboard::Format format, const std::string &expectedContent, int timeoutMs
|
|
)
|
|
{
|
|
auto condition = [&clipboard, format, &expectedContent]() {
|
|
if (!clipboard.has(format)) {
|
|
return false;
|
|
}
|
|
return clipboard.get(format) == expectedContent;
|
|
};
|
|
|
|
return waitForClipboardCondition(clipboard, condition, timeoutMs);
|
|
}
|
|
|
|
QTEST_MAIN(WlClipboardTests)
|