1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
|
/*
* Copyright © 2018 Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Authored by: Alan Griffiths <alan@octopull.co.uk>
*/
#include "in_process_server.h"
#include <gmock/gmock.h>
using namespace testing;
using namespace wlcs;
namespace
{
struct FrameSubmission : StartedInProcessServer
{
Client client{the_server()};
Surface surface{client.create_visible_surface(200, 200)};
void submit_frame(bool& frame_consumed);
void wait_for_frame(bool const& frame_consumed);
};
void FrameSubmission::submit_frame(bool& consumed_flag)
{
static wl_callback_listener const frame_listener
{
[](void *data, struct wl_callback* callback, uint32_t /*callback_data*/)
{
*static_cast<bool*>(data) = true;
wl_callback_destroy(callback);
}
};
consumed_flag = false;
wl_callback_add_listener(wl_surface_frame(surface), &frame_listener, &consumed_flag);
auto buffer = std::make_shared<wlcs::ShmBuffer>(client, 200, 200);
wl_surface_attach(surface, *buffer, 0, 0);
wl_surface_commit(surface);
}
void FrameSubmission::wait_for_frame(bool const& consumed_flag)
{
// TODO timeout
client.dispatch_until([&consumed_flag]() { return consumed_flag; });
}
}
TEST_F(FrameSubmission, post_one_frame_at_a_time)
{
for (auto i = 0; i != 10; ++i)
{
auto frame_consumed = false;
submit_frame(frame_consumed);
wait_for_frame(frame_consumed);
EXPECT_THAT(frame_consumed, Eq(true));
}
}
// Regression test for https://github.com/MirServer/mir/issues/2960
TEST_F(FrameSubmission, test_buffer_can_be_deleted_after_attached)
{
using namespace testing;
wlcs::Client client{the_server()};
auto surface = client.create_visible_surface(200, 200);
auto buffer = std::make_shared<wlcs::ShmBuffer>(client, 200, 200);
wl_surface_attach(surface, *buffer, 0, 0);
buffer.reset();
/* Check that the destroyed buffer doesn't crash the server
* It's not clear what the *correct* behaviour is in this case:
* Weston treats this as committing a null buffer, wlroots appears to
* treat this as committing the content of the buffer before deletion.
*
* *Whatever* the correct behaviour is, "crash" is *definitely*
* incorrect.
*/
wl_surface_commit(surface);
// Roundtrip to ensure the server has processed our weirdness
client.roundtrip();
}
/* Firefox has a lovely habit of sending an endless stream of
* wl_surface.frame() requests (maybe it's trying to estimate vsync?!)
*
* If a compositor responds immediately to the frame on commit, Firefox
* ends up looping endlessly. If the compositor *doesn't* respond to the frame
* request, Firefox decides not to draw anything. 🤷♀️
*/
TEST_F(FrameSubmission, when_client_endlessly_requests_frame_then_callbacks_are_throttled)
{
using namespace testing;
using namespace std::chrono_literals;
wlcs::Client annoying_client{the_server()};
auto surface = client.create_visible_surface(200, 200);
bool frame_callback_called{false};
wl_callback_listener const listener = {
.done = [](void* data, struct wl_callback* callback, [[maybe_unused]]uint32_t timestamp)
{
wl_callback_destroy(callback);
auto callback_called = static_cast<bool*>(data);
*callback_called = true;
}
};
auto const timeout = std::chrono::steady_clock::now() + 10s;
do
{
frame_callback_called = false;
wl_callback_add_listener(wl_surface_frame(surface), &listener, &frame_callback_called);
wl_surface_commit(surface);
client.roundtrip();
/* This roundtrip ensures the server has processed everything prior.
* In particular, if the server sends the frame callback in response to wl_surface.commit, that frame callback
* will have been processed by now.
*/
}
while (frame_callback_called && std::chrono::steady_clock::now() < timeout);
EXPECT_THAT(std::chrono::steady_clock::now(), Lt(timeout)) << "Timed out looping in frame callback storm";
}
/*
* The above test checks that frame callbacks are throttled, but not that
* they are sent at all.
*
* We also need to test that these type of buffer-less frame requests
* *do* get a callback, eventually.
*/
TEST_F(FrameSubmission, frame_requests_without_buffer_are_called_back_eventually)
{
std::array<bool, 5> called{};
wlcs::Client client{the_server()};
auto surface = client.create_visible_surface(640, 480);
for (auto i = 0u; i < called.size(); ++i)
{
surface.add_frame_callback([&called, i](auto) { called[i] = true; });
wl_surface_commit(surface);
}
client.dispatch_until([&called]() { return std::all_of(called.begin(), called.end(), [](bool value) { return value; }); });
}
|