blob: 4e8f3d710817c5e58d77dc08d8f16e2def5eac0c [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>
Eric Caruso5a9aa6e2020-07-01 11:45:11 -070010#include <utility>
Eric Caruso246e1412019-01-24 16:44:02 -080011#include <vector>
12
13#include <base/logging.h>
hscham78e4b8b2020-09-02 14:37:11 +090014#include <base/test/task_environment.h>
Eric Caruso246e1412019-01-24 16:44:02 -080015#include <base/run_loop.h>
16#include <gtest/gtest.h>
17
18#include "glib-bridge/glib_bridge.h"
Eric Caruso5a9aa6e2020-07-01 11:45:11 -070019#include "glib-bridge/glib_scopers.h"
Eric Caruso246e1412019-01-24 16:44:02 -080020
21namespace glib_bridge {
22
23namespace {
Eric Carusobcc58bb2019-11-07 14:10:35 -080024
Eric Caruso246e1412019-01-24 16:44:02 -080025constexpr base::TimeDelta kTestTimeout = base::TimeDelta::FromSeconds(1);
Eric Carusobcc58bb2019-11-07 14:10:35 -080026
27// Use instead of g_idle_add, which implicitly uses the global default context.
28void ScheduleIdleCallback(GSourceFunc func, gpointer data) {
29 GSource* idle_source = g_idle_source_new();
30 g_source_set_callback(idle_source, func, data, nullptr);
31 g_source_set_priority(idle_source, G_PRIORITY_DEFAULT);
32 g_source_attach(idle_source, g_main_context_get_thread_default());
33 g_source_unref(idle_source);
34}
35
36// Use instead of g_timeout_add, which implicitly uses the global default
37// context.
38void ScheduleTimeoutCallback(int timeout_ms, GSourceFunc func, gpointer data) {
39 GSource* timeout_source = g_timeout_source_new(timeout_ms);
40 g_source_set_callback(timeout_source, func, data, nullptr);
41 g_source_set_priority(timeout_source, G_PRIORITY_DEFAULT);
42 g_source_attach(timeout_source, g_main_context_get_thread_default());
43 g_source_unref(timeout_source);
44}
45
Eric Caruso246e1412019-01-24 16:44:02 -080046} // namespace
47
48class GlibBridgeTest : public ::testing::Test {
49 public:
Eric Carusobcc58bb2019-11-07 14:10:35 -080050 GlibBridgeTest() : glib_bridge_(new GlibBridge()) {}
Qijiang Fan6bc59e12020-11-11 02:51:06 +090051 GlibBridgeTest(const GlibBridgeTest&) = delete;
52 GlibBridgeTest& operator=(const GlibBridgeTest&) = delete;
53
Eric Caruso246e1412019-01-24 16:44:02 -080054 ~GlibBridgeTest() override {}
55
56 void Finish() { run_loop_.Quit(); }
57
58 protected:
59 void Start() {
60 // Set up timeout
hscham78e4b8b2020-09-02 14:37:11 +090061 task_environment_.GetMainThreadTaskRunner()->PostDelayedTask(
Eric Caruso246e1412019-01-24 16:44:02 -080062 FROM_HERE, run_loop_.QuitClosure(), kTestTimeout);
63 run_loop_.Run();
64 }
65
66 private:
hscham78e4b8b2020-09-02 14:37:11 +090067 base::test::TaskEnvironment task_environment_{
68 base::test::TaskEnvironment::ThreadingMode::MAIN_THREAD_ONLY,
69 base::test::TaskEnvironment::MainThreadType::IO};
Eric Caruso246e1412019-01-24 16:44:02 -080070 base::RunLoop run_loop_;
71 std::unique_ptr<GlibBridge> glib_bridge_;
Eric Caruso246e1412019-01-24 16:44:02 -080072};
73
74TEST_F(GlibBridgeTest, ReadFileCallback) {
75 struct UserData {
76 GlibBridgeTest* test;
77 ssize_t bytes_read;
78 };
79 UserData user_data{this, 0};
80
Eric Caruso5a9aa6e2020-07-01 11:45:11 -070081 ScopedGObject<GFile> dev_file(g_file_new_for_path("/dev/zero"));
82 ASSERT_TRUE(dev_file);
83 ScopedGObject<GFileInputStream> istream(
84 g_file_read(dev_file.get(), nullptr, nullptr));
85 ASSERT_TRUE(istream);
Eric Caruso246e1412019-01-24 16:44:02 -080086
87 constexpr int kBufSize = 64;
88 char buf[kBufSize];
89 memset(buf, 1, kBufSize);
90 auto read_results_ready = [](GObject* source, GAsyncResult* res,
91 gpointer user_data) {
92 UserData* ud = reinterpret_cast<UserData*>(user_data);
Eric Caruso2e914f72020-07-01 12:05:17 -070093 ud->bytes_read =
94 g_input_stream_read_finish(G_INPUT_STREAM(source), res, nullptr);
Eric Caruso246e1412019-01-24 16:44:02 -080095 ud->test->Finish();
96 };
97 g_input_stream_read_async(
Eric Caruso2e914f72020-07-01 12:05:17 -070098 G_INPUT_STREAM(istream.get()), buf, kBufSize, G_PRIORITY_DEFAULT, nullptr,
Eric Caruso246e1412019-01-24 16:44:02 -080099 static_cast<GAsyncReadyCallback>(read_results_ready), &user_data);
100 Start();
101
102 ASSERT_EQ(user_data.bytes_read, kBufSize);
103 char expected_buf[kBufSize];
104 memset(expected_buf, 0, kBufSize);
105 ASSERT_EQ(memcmp(buf, expected_buf, kBufSize), 0);
Eric Caruso246e1412019-01-24 16:44:02 -0800106}
107
108TEST_F(GlibBridgeTest, WriteFileCallback) {
109 struct UserData {
110 GlibBridgeTest* test;
111 ssize_t bytes_written;
112 };
113 UserData user_data{this, 0};
114
Eric Caruso5a9aa6e2020-07-01 11:45:11 -0700115 ScopedGObject<GFile> dev_file(g_file_new_for_path("/dev/null"));
116 ASSERT_TRUE(dev_file);
117 ScopedGObject<GFileOutputStream> ostream(
118 g_file_append_to(dev_file.get(), G_FILE_CREATE_NONE, nullptr, nullptr));
119 ASSERT_TRUE(ostream);
Eric Caruso246e1412019-01-24 16:44:02 -0800120
121 const std::string buf("foobar");
122 auto write_done = [](GObject* source, GAsyncResult* res, gpointer user_data) {
123 UserData* ud = reinterpret_cast<UserData*>(user_data);
124 ud->bytes_written = g_output_stream_write_finish(
125 reinterpret_cast<GOutputStream*>(source), res, nullptr);
126 ud->test->Finish();
127 };
Eric Caruso2e914f72020-07-01 12:05:17 -0700128 g_output_stream_write_async(G_OUTPUT_STREAM(ostream.get()), buf.data(),
129 buf.size(), G_PRIORITY_DEFAULT, nullptr,
130 static_cast<GAsyncReadyCallback>(write_done),
131 &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800132 Start();
133
134 ASSERT_EQ(user_data.bytes_written, buf.size());
Eric Caruso246e1412019-01-24 16:44:02 -0800135}
136
137TEST_F(GlibBridgeTest, IdleCallback) {
138 struct UserData {
139 GlibBridgeTest* test;
140 bool called;
141 };
142 UserData user_data{this, false};
143
144 auto idle_callback = [](gpointer user_data) {
145 UserData* ud = reinterpret_cast<UserData*>(user_data);
146 ud->called = true;
147 ud->test->Finish();
148 return G_SOURCE_REMOVE;
149 };
150
Eric Carusobcc58bb2019-11-07 14:10:35 -0800151 ScheduleIdleCallback(static_cast<GSourceFunc>(idle_callback), &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800152 Start();
153
154 ASSERT_TRUE(user_data.called);
155}
156
157TEST_F(GlibBridgeTest, TimeoutOnceCallback) {
158 struct UserData {
159 GlibBridgeTest* test;
160 bool called;
161 };
162 UserData user_data{this, false};
163
164 auto timer_callback = [](gpointer user_data) {
165 UserData* ud = reinterpret_cast<UserData*>(user_data);
166 ud->called = true;
167 ud->test->Finish();
168 return G_SOURCE_REMOVE;
169 };
170
171 constexpr uint kTimeoutIntervalMs = 200;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800172 ScheduleTimeoutCallback(kTimeoutIntervalMs,
173 static_cast<GSourceFunc>(timer_callback), &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800174 Start();
175
176 ASSERT_TRUE(user_data.called);
177}
178
179TEST_F(GlibBridgeTest, TimeoutMultiCallback) {
180 constexpr int kTarget = 5;
181 struct UserData {
182 GlibBridgeTest* test;
183 int counter;
184 };
185 UserData user_data{this, 0};
186
187 auto timer_callback = [](gpointer user_data) -> gboolean {
188 UserData* ud = reinterpret_cast<UserData*>(user_data);
189 ud->counter++;
190 if (ud->counter == kTarget) {
191 ud->test->Finish();
192 return G_SOURCE_REMOVE;
193 }
194 return G_SOURCE_CONTINUE;
195 };
196
197 constexpr uint kTimeoutIntervalMs = 100;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800198 ScheduleTimeoutCallback(kTimeoutIntervalMs,
199 static_cast<GSourceFunc>(timer_callback), &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800200 Start();
201
202 ASSERT_EQ(user_data.counter, kTarget);
203}
204
205TEST_F(GlibBridgeTest, MultipleTimeouts) {
206 constexpr uint kNumFlags = 5;
207 struct UserData {
208 GlibBridgeTest* test;
209 int counter;
210 bool called[kNumFlags];
211 };
212 UserData user_data{this, 0, {false}};
213
214 auto timer_callback = [](gpointer user_data) {
215 UserData* ud = reinterpret_cast<UserData*>(user_data);
216 ud->called[ud->counter] = true;
217 ud->counter++;
218 if (ud->counter == kNumFlags)
219 ud->test->Finish();
220 return G_SOURCE_REMOVE;
221 };
222
223 constexpr uint kTimeoutIntervalMs = 100;
224 for (int i = 0; i < kNumFlags; i++) {
Eric Carusobcc58bb2019-11-07 14:10:35 -0800225 ScheduleTimeoutCallback(kTimeoutIntervalMs * (i + 1),
226 static_cast<GSourceFunc>(timer_callback),
227 &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800228 }
229 Start();
230
231 for (int i = 0; i < kNumFlags; i++)
232 ASSERT_TRUE(user_data.called[i]);
233}
234
235namespace multi_io_test {
236
237constexpr int kBufSize = 64;
238
239struct UserData;
240struct IoJob {
Eric Caruso5a9aa6e2020-07-01 11:45:11 -0700241 IoJob(ScopedGObject<GFile> file,
242 ScopedGObject<GFileInputStream> istream,
243 UserData* user_data)
244 : file(std::move(file)),
245 istream(std::move(istream)),
246 buf(kBufSize, 1),
247 user_data(user_data) {}
Eric Caruso246e1412019-01-24 16:44:02 -0800248
Eric Caruso5a9aa6e2020-07-01 11:45:11 -0700249 ScopedGObject<GFile> file;
250 ScopedGObject<GFileInputStream> istream;
Eric Caruso246e1412019-01-24 16:44:02 -0800251 std::vector<char> buf;
252 bool complete = false;
253 UserData* user_data;
254};
255
256struct UserData {
257 GlibBridgeTest* test;
258 std::vector<IoJob> io_jobs;
259};
260
261gboolean AllCompleteCheck(gpointer user_data) {
262 UserData* ud = reinterpret_cast<UserData*>(user_data);
263 bool all_complete = true;
264 for (const IoJob& io_job : ud->io_jobs)
265 all_complete &= io_job.complete;
266 if (all_complete)
267 ud->test->Finish();
268 return G_SOURCE_REMOVE;
269}
270
271void ReadResultsReady(GObject* source, GAsyncResult* res, gpointer user_data) {
272 IoJob* job = reinterpret_cast<IoJob*>(user_data);
273 job->complete =
Eric Caruso2e914f72020-07-01 12:05:17 -0700274 g_input_stream_read_finish(G_INPUT_STREAM(source), res, nullptr) >= 0;
Eric Carusobcc58bb2019-11-07 14:10:35 -0800275 ScheduleIdleCallback(static_cast<GSourceFunc>(&AllCompleteCheck),
276 job->user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800277}
278
279TEST_F(GlibBridgeTest, MultipleReadAndIdleCallbacks) {
280 UserData user_data{this};
281 constexpr int kNumFiles = 5;
282 for (int i = 0; i < kNumFiles; i++) {
Eric Caruso5a9aa6e2020-07-01 11:45:11 -0700283 ScopedGObject<GFile> dev_file(g_file_new_for_path("/dev/zero"));
284 ASSERT_TRUE(dev_file);
285 ScopedGObject<GFileInputStream> istream(
286 g_file_read(dev_file.get(), nullptr, nullptr));
287 ASSERT_TRUE(istream);
288 user_data.io_jobs.emplace_back(std::move(dev_file), std::move(istream),
289 &user_data);
Eric Caruso246e1412019-01-24 16:44:02 -0800290 }
291
292 for (IoJob& io_job : user_data.io_jobs) {
293 g_input_stream_read_async(
Eric Caruso2e914f72020-07-01 12:05:17 -0700294 G_INPUT_STREAM(io_job.istream.get()), &io_job.buf[0], io_job.buf.size(),
295 G_PRIORITY_DEFAULT, nullptr,
Eric Caruso246e1412019-01-24 16:44:02 -0800296 static_cast<GAsyncReadyCallback>(&ReadResultsReady), &io_job);
297 }
298 Start();
299
300 bool all_complete = true;
301 for (const IoJob& io_job : user_data.io_jobs) {
302 all_complete &= io_job.complete;
303 std::vector<char> expected_buf(io_job.buf.size(), 0);
304 ASSERT_EQ(io_job.buf, expected_buf);
Eric Caruso246e1412019-01-24 16:44:02 -0800305 }
306 ASSERT_TRUE(all_complete);
307}
308
309} // namespace multi_io_test
310
311} // namespace glib_bridge