blob: 22dbe6c361f3850e2b2cddf0aef6272976ada9f4 [file] [log] [blame]
Eric Caruso246e1412019-01-24 16:44:02 -08001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <gio/gio.h>
6#include <glib.h>
7
8#include <memory>
9#include <string>
10#include <vector>
11
12#include <base/logging.h>
13#include <base/message_loop/message_loop.h>
14#include <base/run_loop.h>
15#include <gtest/gtest.h>
16
17#include "glib-bridge/glib_bridge.h"
18
19namespace glib_bridge {
20
21namespace {
Eric Carusobcc58bb2019-11-07 14:10:35 -080022
Eric Caruso246e1412019-01-24 16:44:02 -080023constexpr base::TimeDelta kTestTimeout = base::TimeDelta::FromSeconds(1);
Eric Carusobcc58bb2019-11-07 14:10:35 -080024
25// Use instead of g_idle_add, which implicitly uses the global default context.
26void ScheduleIdleCallback(GSourceFunc func, gpointer data) {
27 GSource* idle_source = g_idle_source_new();
28 g_source_set_callback(idle_source, func, data, nullptr);
29 g_source_set_priority(idle_source, G_PRIORITY_DEFAULT);
30 g_source_attach(idle_source, g_main_context_get_thread_default());
31 g_source_unref(idle_source);
32}
33
34// Use instead of g_timeout_add, which implicitly uses the global default
35// context.
36void ScheduleTimeoutCallback(int timeout_ms, GSourceFunc func, gpointer data) {
37 GSource* timeout_source = g_timeout_source_new(timeout_ms);
38 g_source_set_callback(timeout_source, func, data, nullptr);
39 g_source_set_priority(timeout_source, G_PRIORITY_DEFAULT);
40 g_source_attach(timeout_source, g_main_context_get_thread_default());
41 g_source_unref(timeout_source);
42}
43
Eric Caruso246e1412019-01-24 16:44:02 -080044} // namespace
45
46class GlibBridgeTest : public ::testing::Test {
47 public:
Eric Carusobcc58bb2019-11-07 14:10:35 -080048 GlibBridgeTest() : glib_bridge_(new GlibBridge()) {}
Eric Caruso246e1412019-01-24 16:44:02 -080049 ~GlibBridgeTest() override {}
50
51 void Finish() { run_loop_.Quit(); }
52
53 protected:
54 void Start() {
55 // Set up timeout
56 message_loop_.task_runner()->PostDelayedTask(
57 FROM_HERE, run_loop_.QuitClosure(), kTestTimeout);
58 run_loop_.Run();
59 }
60
61 private:
62 base::MessageLoopForIO message_loop_;
63 base::RunLoop run_loop_;
Eric Carusobcc58bb2019-11-07 14:10:35 -080064 base::FileDescriptorWatcher watcher_{&message_loop_};
Eric Caruso246e1412019-01-24 16:44:02 -080065 std::unique_ptr<GlibBridge> glib_bridge_;
66
67 DISALLOW_COPY_AND_ASSIGN(GlibBridgeTest);
68};
69
70TEST_F(GlibBridgeTest, ReadFileCallback) {
71 struct UserData {
72 GlibBridgeTest* test;
73 ssize_t bytes_read;
74 };
75 UserData user_data{this, 0};
76
77 GFile* dev_file = g_file_new_for_path("/dev/zero");
78 ASSERT_NE(dev_file, nullptr);
79 GFileInputStream* istream = g_file_read(dev_file, nullptr, nullptr);
80 ASSERT_NE(istream, nullptr);
81
82 constexpr int kBufSize = 64;
83 char buf[kBufSize];
84 memset(buf, 1, kBufSize);
85 auto read_results_ready = [](GObject* source, GAsyncResult* res,
86 gpointer user_data) {
87 UserData* ud = reinterpret_cast<UserData*>(user_data);
88 ud->bytes_read = g_input_stream_read_finish(
89 reinterpret_cast<GInputStream*>(source), res, nullptr);
90 ud->test->Finish();
91 };
92 g_input_stream_read_async(
93 reinterpret_cast<GInputStream*>(istream), buf, kBufSize,
94 G_PRIORITY_DEFAULT, nullptr,
95 static_cast<GAsyncReadyCallback>(read_results_ready), &user_data);
96 Start();
97
98 ASSERT_EQ(user_data.bytes_read, kBufSize);
99 char expected_buf[kBufSize];
100 memset(expected_buf, 0, kBufSize);
101 ASSERT_EQ(memcmp(buf, expected_buf, kBufSize), 0);
102
103 g_object_unref(istream);
104 g_object_unref(dev_file);
105}
106
107TEST_F(GlibBridgeTest, WriteFileCallback) {
108 struct UserData {
109 GlibBridgeTest* test;
110 ssize_t bytes_written;
111 };
112 UserData user_data{this, 0};
113
114 GFile* dev_file = g_file_new_for_path("/dev/null");
115 ASSERT_NE(dev_file, nullptr);
116 GFileOutputStream* ostream =
117 g_file_append_to(dev_file, G_FILE_CREATE_NONE, nullptr, nullptr);
118 ASSERT_NE(ostream, nullptr);
119
120 const std::string buf("foobar");
121 auto write_done = [](GObject* source, GAsyncResult* res, gpointer user_data) {
122 UserData* ud = reinterpret_cast<UserData*>(user_data);
123 ud->bytes_written = g_output_stream_write_finish(
124 reinterpret_cast<GOutputStream*>(source), res, nullptr);
125 ud->test->Finish();
126 };
127 g_output_stream_write_async(
128 reinterpret_cast<GOutputStream*>(ostream), buf.data(), buf.size(),
129 G_PRIORITY_DEFAULT, nullptr, static_cast<GAsyncReadyCallback>(write_done),
130 &user_data);
131 Start();
132
133 ASSERT_EQ(user_data.bytes_written, buf.size());
134
135 g_object_unref(ostream);
136 g_object_unref(dev_file);
137}
138
139TEST_F(GlibBridgeTest, IdleCallback) {
140 struct UserData {
141 GlibBridgeTest* test;
142 bool called;
143 };
144 UserData user_data{this, false};
145
146 auto idle_callback = [](gpointer user_data) {
147 UserData* ud = reinterpret_cast<UserData*>(user_data);
148 ud->called = true;
149 ud->test->Finish();
150 return G_SOURCE_REMOVE;
151 };
152
Eric Carusobcc58bb2019-11-07 14:10:35 -0800153 ScheduleIdleCallback(static_cast<GSourceFunc>(idle_callback), &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800154 Start();
155
156 ASSERT_TRUE(user_data.called);
157}
158
159TEST_F(GlibBridgeTest, TimeoutOnceCallback) {
160 struct UserData {
161 GlibBridgeTest* test;
162 bool called;
163 };
164 UserData user_data{this, false};
165
166 auto timer_callback = [](gpointer user_data) {
167 UserData* ud = reinterpret_cast<UserData*>(user_data);
168 ud->called = true;
169 ud->test->Finish();
170 return G_SOURCE_REMOVE;
171 };
172
173 constexpr uint kTimeoutIntervalMs = 200;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800174 ScheduleTimeoutCallback(kTimeoutIntervalMs,
175 static_cast<GSourceFunc>(timer_callback), &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800176 Start();
177
178 ASSERT_TRUE(user_data.called);
179}
180
181TEST_F(GlibBridgeTest, TimeoutMultiCallback) {
182 constexpr int kTarget = 5;
183 struct UserData {
184 GlibBridgeTest* test;
185 int counter;
186 };
187 UserData user_data{this, 0};
188
189 auto timer_callback = [](gpointer user_data) -> gboolean {
190 UserData* ud = reinterpret_cast<UserData*>(user_data);
191 ud->counter++;
192 if (ud->counter == kTarget) {
193 ud->test->Finish();
194 return G_SOURCE_REMOVE;
195 }
196 return G_SOURCE_CONTINUE;
197 };
198
199 constexpr uint kTimeoutIntervalMs = 100;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800200 ScheduleTimeoutCallback(kTimeoutIntervalMs,
201 static_cast<GSourceFunc>(timer_callback), &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800202 Start();
203
204 ASSERT_EQ(user_data.counter, kTarget);
205}
206
207TEST_F(GlibBridgeTest, MultipleTimeouts) {
208 constexpr uint kNumFlags = 5;
209 struct UserData {
210 GlibBridgeTest* test;
211 int counter;
212 bool called[kNumFlags];
213 };
214 UserData user_data{this, 0, {false}};
215
216 auto timer_callback = [](gpointer user_data) {
217 UserData* ud = reinterpret_cast<UserData*>(user_data);
218 ud->called[ud->counter] = true;
219 ud->counter++;
220 if (ud->counter == kNumFlags)
221 ud->test->Finish();
222 return G_SOURCE_REMOVE;
223 };
224
225 constexpr uint kTimeoutIntervalMs = 100;
226 for (int i = 0; i < kNumFlags; i++) {
Eric Carusobcc58bb2019-11-07 14:10:35 -0800227 ScheduleTimeoutCallback(kTimeoutIntervalMs * (i + 1),
228 static_cast<GSourceFunc>(timer_callback),
229 &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800230 }
231 Start();
232
233 for (int i = 0; i < kNumFlags; i++)
234 ASSERT_TRUE(user_data.called[i]);
235}
236
237namespace multi_io_test {
238
239constexpr int kBufSize = 64;
240
241struct UserData;
242struct IoJob {
243 IoJob(GFile* file, GFileInputStream* istream, UserData* user_data)
244 : file(file), istream(istream), buf(kBufSize, 1), user_data(user_data) {}
245
246 GFile* file;
247 GFileInputStream* istream;
248 std::vector<char> buf;
249 bool complete = false;
250 UserData* user_data;
251};
252
253struct UserData {
254 GlibBridgeTest* test;
255 std::vector<IoJob> io_jobs;
256};
257
258gboolean AllCompleteCheck(gpointer user_data) {
259 UserData* ud = reinterpret_cast<UserData*>(user_data);
260 bool all_complete = true;
261 for (const IoJob& io_job : ud->io_jobs)
262 all_complete &= io_job.complete;
263 if (all_complete)
264 ud->test->Finish();
265 return G_SOURCE_REMOVE;
266}
267
268void ReadResultsReady(GObject* source, GAsyncResult* res, gpointer user_data) {
269 IoJob* job = reinterpret_cast<IoJob*>(user_data);
270 job->complete =
271 g_input_stream_read_finish(reinterpret_cast<GInputStream*>(source), res,
272 nullptr) >= 0;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800273 ScheduleIdleCallback(static_cast<GSourceFunc>(&AllCompleteCheck),
274 job->user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800275}
276
277TEST_F(GlibBridgeTest, MultipleReadAndIdleCallbacks) {
278 UserData user_data{this};
279 constexpr int kNumFiles = 5;
280 for (int i = 0; i < kNumFiles; i++) {
281 GFile* dev_file = g_file_new_for_path("/dev/zero");
282 ASSERT_NE(dev_file, nullptr);
283 GFileInputStream* istream = g_file_read(dev_file, nullptr, nullptr);
284 ASSERT_NE(istream, nullptr);
285 user_data.io_jobs.emplace_back(dev_file, istream, &user_data);
286 }
287
288 for (IoJob& io_job : user_data.io_jobs) {
289 g_input_stream_read_async(
290 reinterpret_cast<GInputStream*>(io_job.istream), &io_job.buf[0],
291 io_job.buf.size(), G_PRIORITY_DEFAULT, nullptr,
292 static_cast<GAsyncReadyCallback>(&ReadResultsReady), &io_job);
293 }
294 Start();
295
296 bool all_complete = true;
297 for (const IoJob& io_job : user_data.io_jobs) {
298 all_complete &= io_job.complete;
299 std::vector<char> expected_buf(io_job.buf.size(), 0);
300 ASSERT_EQ(io_job.buf, expected_buf);
301
302 g_object_unref(io_job.istream);
303 g_object_unref(io_job.file);
304 }
305 ASSERT_TRUE(all_complete);
306}
307
308} // namespace multi_io_test
309
310} // namespace glib_bridge