2
* Copyright © 2013 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License version 3 as
6
* published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
19
#include "src/platform/graphics/mesa/anonymous_shm_file.h"
21
#include <gtest/gtest.h>
22
#include <gmock/gmock.h>
24
#include <boost/throw_exception.hpp>
32
#include <sys/inotify.h>
34
namespace mg = mir::graphics;
35
namespace mgm = mir::graphics::mesa;
40
class TemporaryEnvironmentValue
43
TemporaryEnvironmentValue(char const* name, char const* value)
45
has_old_value{getenv(name) != nullptr},
46
old_value{has_old_value ? getenv(name) : ""}
49
setenv(name, value, overwrite);
54
~TemporaryEnvironmentValue()
57
setenv(name.c_str(), old_value.c_str(), overwrite);
59
unsetenv(name.c_str());
63
static int const overwrite = 1;
64
std::string const name;
65
bool const has_old_value;
66
std::string const old_value;
69
class TemporaryDirectory
74
char tmpl[] = "/tmp/mir-test-dir-XXXXXX";
75
if (mkdtemp(tmpl) == nullptr)
77
BOOST_THROW_EXCEPTION(
78
std::runtime_error("Couldn't create temporary test directory"));
88
char const* path() const
100
PathWatcher(const char* path)
101
: inotify_fd{inotify_init1(IN_NONBLOCK)},
102
watch_fd{inotify_add_watch(inotify_fd, path,
103
IN_CREATE | IN_DELETE)}
108
BOOST_THROW_EXCEPTION(
109
std::runtime_error("Couldn't create inotify fd"));
114
BOOST_THROW_EXCEPTION(
115
std::runtime_error("Couldn't create watch fd"));
121
inotify_rm_watch(inotify_fd, watch_fd);
125
void process_events() const
127
while (process_events_step()) continue;
130
MOCK_CONST_METHOD1(file_created, void(char const*));
131
MOCK_CONST_METHOD1(file_deleted, void(char const*));
134
bool process_events_step() const
136
size_t const max_path_size = 1024;
137
size_t const buffer_size = sizeof(struct inotify_event) + max_path_size;
138
uint8_t buffer[buffer_size];
140
auto nread = read(inotify_fd, buffer, buffer_size);
144
if (errno == EWOULDBLOCK)
150
BOOST_THROW_EXCEPTION(
151
std::runtime_error("Couldn't read from inotify fd"));
158
auto event = reinterpret_cast<struct inotify_event*>(&buffer[i]);
161
if (event->mask & IN_CREATE)
163
file_created(event->name);
166
if (event->mask & IN_DELETE)
168
file_deleted(event->name);
171
i += sizeof(struct inotify_event) + event->len;
177
int const inotify_fd;
183
TEST(AnonymousShmFile, is_created_and_deleted_in_xdg_runtime_dir)
185
using namespace testing;
187
TemporaryDirectory const temp_dir;
188
TemporaryEnvironmentValue const env{"XDG_RUNTIME_DIR", temp_dir.path()};
189
PathWatcher const path_watcher{temp_dir.path()};
190
size_t const file_size{100};
193
EXPECT_CALL(path_watcher, file_created(StartsWith("mir-buffer-")));
194
EXPECT_CALL(path_watcher, file_deleted(StartsWith("mir-buffer-")));
196
mgm::AnonymousShmFile shm_file{file_size};
198
path_watcher.process_events();
201
TEST(AnonymousShmFile, is_created_and_deleted_in_tmp_dir)
203
using namespace testing;
205
TemporaryEnvironmentValue const env{"XDG_RUNTIME_DIR", nullptr};
206
PathWatcher const path_watcher{"/tmp"};
207
size_t const file_size{100};
210
EXPECT_CALL(path_watcher, file_created(StartsWith("mir-buffer-")));
211
EXPECT_CALL(path_watcher, file_deleted(StartsWith("mir-buffer-")));
213
mgm::AnonymousShmFile shm_file{file_size};
215
path_watcher.process_events();
218
TEST(AnonymousShmFile, has_correct_size)
220
using namespace testing;
222
TemporaryDirectory const temp_dir;
223
TemporaryEnvironmentValue const env{"XDG_RUNTIME_DIR", temp_dir.path()};
224
size_t const file_size{100};
226
mgm::AnonymousShmFile shm_file{file_size};
229
fstat(shm_file.fd(), &stat);
231
EXPECT_EQ(static_cast<off_t>(file_size), stat.st_size);
234
TEST(AnonymousShmFile, writing_to_base_ptr_writes_to_file)
236
using namespace testing;
238
TemporaryDirectory const temp_dir;
239
TemporaryEnvironmentValue const env{"XDG_RUNTIME_DIR", temp_dir.path()};
240
size_t const file_size{100};
242
mgm::AnonymousShmFile shm_file{file_size};
244
auto base_ptr = reinterpret_cast<uint8_t*>(shm_file.base_ptr());
246
for (size_t i = 0; i < file_size; i++)
251
std::vector<unsigned char> buffer(file_size);
253
EXPECT_EQ(static_cast<ssize_t>(file_size),
254
read(shm_file.fd(), buffer.data(), file_size));
256
for (size_t i = 0; i < file_size; i++)
258
EXPECT_EQ(base_ptr[i], buffer[i]) << "i=" << i;