~kgunn72/mir/dont-crash-when-shooting-invalid-surface

« back to all changes in this revision

Viewing changes to tests/unit-tests/graphics/mesa/test_anonymous_shm_file.cpp

  • Committer: Tarmac
  • Author(s): Alan Griffiths, Alexandros Frantzis, Daniel van Vugt, Kevin DuBois, Christopher James Halse Rogers, chris.gagnon, Mathieu Trudel-Lapierre, Robert Carr, Automatic PS uploader, Kevin Gunn, Daniel d'Andrada, Christopher James Halse Rogers, Michael Terry, Brandon Schaefer, Timo Jyrinki, Mir Team, Andreas Pokorny
  • Date: 2013-12-20 11:11:22 UTC
  • mfrom: (1169.1.1 version-0.1.3)
  • Revision ID: tarmac-20131220111122-h503fd1fnq7pn9za
Latest upstream release: Mir 0.1.3 (r1298).

Approved by Alan Griffiths, PS Jenkins bot.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright © 2013 Canonical Ltd.
 
3
 *
 
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.
 
7
 *
 
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.
 
12
 *
 
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/>.
 
15
 *
 
16
 * Authored by: Alexandros Frantzis <alexandros.frantzis@canonical.com>
 
17
 */
 
18
 
 
19
#include "src/platform/graphics/mesa/anonymous_shm_file.h"
 
20
 
 
21
#include <gtest/gtest.h>
 
22
#include <gmock/gmock.h>
 
23
 
 
24
#include <boost/throw_exception.hpp>
 
25
#include <stdexcept>
 
26
#include <string>
 
27
#include <atomic>
 
28
#include <thread>
 
29
 
 
30
#include <cstdlib>
 
31
#include <unistd.h>
 
32
#include <sys/inotify.h>
 
33
 
 
34
namespace mg = mir::graphics;
 
35
namespace mgm = mir::graphics::mesa;
 
36
 
 
37
namespace
 
38
{
 
39
 
 
40
class TemporaryEnvironmentValue
 
41
{
 
42
public:
 
43
    TemporaryEnvironmentValue(char const* name, char const* value)
 
44
        : name{name},
 
45
          has_old_value{getenv(name) != nullptr},
 
46
          old_value{has_old_value ? getenv(name) : ""}
 
47
    {
 
48
        if (value)
 
49
            setenv(name, value, overwrite);
 
50
        else
 
51
            unsetenv(name);
 
52
    }
 
53
 
 
54
    ~TemporaryEnvironmentValue()
 
55
    {
 
56
        if (has_old_value)
 
57
            setenv(name.c_str(), old_value.c_str(), overwrite);
 
58
        else
 
59
            unsetenv(name.c_str());
 
60
    }
 
61
 
 
62
private:
 
63
    static int const overwrite = 1;
 
64
    std::string const name;
 
65
    bool const has_old_value;
 
66
    std::string const old_value;
 
67
};
 
68
 
 
69
class TemporaryDirectory
 
70
{
 
71
public:
 
72
    TemporaryDirectory()
 
73
    {
 
74
        char tmpl[] = "/tmp/mir-test-dir-XXXXXX";
 
75
        if (mkdtemp(tmpl) == nullptr)
 
76
        {
 
77
            BOOST_THROW_EXCEPTION(
 
78
                std::runtime_error("Couldn't create temporary test directory"));
 
79
        }
 
80
        path_ = tmpl;
 
81
    }
 
82
 
 
83
    ~TemporaryDirectory()
 
84
    {
 
85
        rmdir(path());
 
86
    }
 
87
 
 
88
    char const* path() const
 
89
    {
 
90
        return path_.c_str();
 
91
    }
 
92
 
 
93
private:
 
94
    std::string path_;
 
95
};
 
96
 
 
97
class PathWatcher
 
98
{
 
99
public:
 
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)}
 
104
    {
 
105
        /* TODO: RAII fd */
 
106
        if (inotify_fd < 0)
 
107
        {
 
108
            BOOST_THROW_EXCEPTION(
 
109
                std::runtime_error("Couldn't create inotify fd"));
 
110
        }
 
111
        if (watch_fd < 0)
 
112
        {
 
113
            close(inotify_fd);
 
114
            BOOST_THROW_EXCEPTION(
 
115
                std::runtime_error("Couldn't create watch fd"));
 
116
        }
 
117
    }
 
118
 
 
119
    ~PathWatcher()
 
120
    {
 
121
        inotify_rm_watch(inotify_fd, watch_fd);
 
122
        close(inotify_fd);
 
123
    }
 
124
 
 
125
    void process_events() const
 
126
    {
 
127
        while (process_events_step()) continue;
 
128
    }
 
129
 
 
130
    MOCK_CONST_METHOD1(file_created, void(char const*));
 
131
    MOCK_CONST_METHOD1(file_deleted, void(char const*));
 
132
    
 
133
private:
 
134
    bool process_events_step() const
 
135
    {
 
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];
 
139
 
 
140
        auto nread = read(inotify_fd, buffer, buffer_size); 
 
141
 
 
142
        if (nread < 0)
 
143
        {
 
144
            if (errno == EWOULDBLOCK)
 
145
            {
 
146
                return false;
 
147
            }
 
148
            else
 
149
            {
 
150
                BOOST_THROW_EXCEPTION(
 
151
                    std::runtime_error("Couldn't read from inotify fd"));
 
152
            }
 
153
        } 
 
154
 
 
155
        ssize_t i = 0;
 
156
        while (i < nread)
 
157
        {
 
158
            auto event = reinterpret_cast<struct inotify_event*>(&buffer[i]);
 
159
            if (event->len)
 
160
            {
 
161
                if (event->mask & IN_CREATE)
 
162
                {
 
163
                    file_created(event->name);
 
164
                }
 
165
 
 
166
                if (event->mask & IN_DELETE)
 
167
                {
 
168
                    file_deleted(event->name);
 
169
                } 
 
170
            }
 
171
            i += sizeof(struct inotify_event) + event->len;
 
172
        }
 
173
 
 
174
        return true;
 
175
    }
 
176
 
 
177
    int const inotify_fd;
 
178
    int const watch_fd;
 
179
};
 
180
 
 
181
}
 
182
 
 
183
TEST(AnonymousShmFile, is_created_and_deleted_in_xdg_runtime_dir)
 
184
{
 
185
    using namespace testing;
 
186
    
 
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};
 
191
 
 
192
    InSequence s;
 
193
    EXPECT_CALL(path_watcher, file_created(StartsWith("mir-buffer-")));
 
194
    EXPECT_CALL(path_watcher, file_deleted(StartsWith("mir-buffer-")));
 
195
 
 
196
    mgm::AnonymousShmFile shm_file{file_size};
 
197
 
 
198
    path_watcher.process_events();
 
199
}
 
200
 
 
201
TEST(AnonymousShmFile, is_created_and_deleted_in_tmp_dir)
 
202
{
 
203
    using namespace testing;
 
204
    
 
205
    TemporaryEnvironmentValue const env{"XDG_RUNTIME_DIR", nullptr};
 
206
    PathWatcher const path_watcher{"/tmp"};
 
207
    size_t const file_size{100};
 
208
 
 
209
    InSequence s;
 
210
    EXPECT_CALL(path_watcher, file_created(StartsWith("mir-buffer-")));
 
211
    EXPECT_CALL(path_watcher, file_deleted(StartsWith("mir-buffer-")));
 
212
 
 
213
    mgm::AnonymousShmFile shm_file{file_size};
 
214
 
 
215
    path_watcher.process_events();
 
216
}
 
217
 
 
218
TEST(AnonymousShmFile, has_correct_size)
 
219
{
 
220
    using namespace testing;
 
221
    
 
222
    TemporaryDirectory const temp_dir;
 
223
    TemporaryEnvironmentValue const env{"XDG_RUNTIME_DIR", temp_dir.path()};
 
224
    size_t const file_size{100};
 
225
 
 
226
    mgm::AnonymousShmFile shm_file{file_size};
 
227
 
 
228
    struct stat stat;
 
229
    fstat(shm_file.fd(), &stat);
 
230
 
 
231
    EXPECT_EQ(static_cast<off_t>(file_size), stat.st_size);
 
232
}
 
233
 
 
234
TEST(AnonymousShmFile, writing_to_base_ptr_writes_to_file)
 
235
{
 
236
    using namespace testing;
 
237
    
 
238
    TemporaryDirectory const temp_dir;
 
239
    TemporaryEnvironmentValue const env{"XDG_RUNTIME_DIR", temp_dir.path()};
 
240
    size_t const file_size{100};
 
241
 
 
242
    mgm::AnonymousShmFile shm_file{file_size};
 
243
 
 
244
    auto base_ptr = reinterpret_cast<uint8_t*>(shm_file.base_ptr());
 
245
 
 
246
    for (size_t i = 0; i < file_size; i++)
 
247
    {
 
248
        base_ptr[i] = i;
 
249
    }
 
250
 
 
251
    std::vector<unsigned char> buffer(file_size);
 
252
 
 
253
    EXPECT_EQ(static_cast<ssize_t>(file_size),
 
254
              read(shm_file.fd(), buffer.data(), file_size));
 
255
 
 
256
    for (size_t i = 0; i < file_size; i++)
 
257
    {
 
258
        EXPECT_EQ(base_ptr[i], buffer[i]) << "i=" << i;
 
259
    }
 
260
}