2
* Copyright (C) 2013-2017 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify
5
* it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Jussi Pakkanen <jussi.pakkanen@canonical.com>
17
* Pete Woods <pete.woods@canonical.com>
20
#include <unity/util/GObjectMemory.h>
21
#include <glib-object.h>
22
#include <gtest/gtest.h>
25
#include <unordered_set>
28
using namespace unity::util;
33
typedef pair<string, int> Deleted;
34
static list<Deleted> DELETED_OBJECTS;
37
// Below here is a basic implementation of a GObject type
44
#define FOO_TYPE_BAR foo_bar_get_type()
45
G_DECLARE_FINAL_TYPE (FooBar, foo_bar, FOO, BAR, GObject)
47
FooBar *foo_bar_new();
49
FooBar *foo_bar_new_full(const gchar* const name, guint id);
53
// private implementation
57
GObject parent_instance;
64
G_DEFINE_TYPE (FooBar, foo_bar, G_TYPE_OBJECT)
73
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
75
static void foo_bar_set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
77
FooBar *self = FOO_BAR(object);
83
self->name = g_value_dup_string(value);
87
self->id = g_value_get_uint(value);
91
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
96
static void foo_bar_get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
98
FooBar *self = FOO_BAR(object);
103
g_value_set_string(value, self->name);
107
g_value_set_uint(value, self->id);
111
G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
116
static void foo_bar_finalize(GObject *gobject)
118
FooBar* self = FOO_BAR(gobject);
120
DELETED_OBJECTS.emplace_back(Deleted(string(self->name == NULL ? "" : self->name), self->id));
124
G_OBJECT_CLASS (foo_bar_parent_class)->finalize(gobject);
127
static void foo_bar_class_init(FooBarClass *klass)
129
GObjectClass *object_class = G_OBJECT_CLASS (klass);
131
object_class->set_property = foo_bar_set_property;
132
object_class->get_property = foo_bar_get_property;
133
object_class->finalize = foo_bar_finalize;
135
obj_properties[PROP_NAME] =
136
g_param_spec_string ("name",
138
"Name of the file to load and display from.",
139
"default-name" /* default value */,
140
(GParamFlags) (G_PARAM_READWRITE));
142
obj_properties[PROP_ID] =
143
g_param_spec_uint ("id",
145
"ID of this dummy class",
146
0 /* minimum value */,
147
10 /* maximum value */,
148
2 /* default value */,
149
(GParamFlags) G_PARAM_READWRITE);
151
g_object_class_install_properties (object_class,
156
static void foo_bar_init(FooBar *)
160
FooBar *foo_bar_new()
162
return FOO_BAR(g_object_new(FOO_TYPE_BAR, NULL));
165
FooBar *foo_bar_new_full(const gchar* const name, guint id)
167
return FOO_BAR(g_object_new(FOO_TYPE_BAR, "name", name, "id", id, NULL));
174
class GObjectMemoryTest: public testing::Test
177
void SetUp() override
179
DELETED_OBJECTS.clear();
183
TEST_F(GObjectMemoryTest, trivial)
185
auto basic = unique_gobject(foo_bar_new());
186
EXPECT_TRUE(bool(basic));
187
EXPECT_TRUE(G_IS_OBJECT(basic.get()));
190
TEST_F(GObjectMemoryTest, compare)
192
FooBar* o1 = foo_bar_new();
193
FooBar* o2 = foo_bar_new();
197
ASSERT_TRUE(o1 < o2);
198
auto u1 = unique_gobject(o1);
199
auto u2 = unique_gobject(o2);
201
EXPECT_TRUE(!(u1 == nullptr));
202
EXPECT_TRUE(u1 != nullptr);
203
EXPECT_TRUE(u1 != u2);
204
EXPECT_TRUE(!(u1 == u2));
205
EXPECT_TRUE(u1 < u2);
206
EXPECT_TRUE(!(u2 < u1));
207
EXPECT_TRUE(!(u1 == u2));
208
EXPECT_TRUE(!(u2 == u1));
209
EXPECT_TRUE(u1 <= u2);
210
EXPECT_TRUE(!(u2 <= u1));
213
// This is its own thing due to need to avoid double release.
215
TEST_F(GObjectMemoryTest, equality)
217
FooBar* o = foo_bar_new();
218
auto u1 = unique_gobject(o);
220
auto u2 = unique_gobject(o);
221
EXPECT_TRUE(u1 == u2);
222
EXPECT_TRUE(u2 == u1);
223
EXPECT_TRUE(!(u1 != u2));
224
EXPECT_TRUE(!(u2 != u1));
227
TEST_F(GObjectMemoryTest, release)
229
FooBar* o = foo_bar_new();
230
auto u = unique_gobject(o);
231
EXPECT_TRUE(u != nullptr);
232
EXPECT_TRUE(u.get() != nullptr);
233
EXPECT_TRUE(o == u.release());
235
EXPECT_TRUE(u.get() == nullptr);
239
TEST_F(GObjectMemoryTest, refcount)
241
GObject* o = G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr));
242
EXPECT_EQ(1, o->ref_count);
246
EXPECT_EQ(2, o->ref_count);
247
auto u = unique_gobject<GObject>(o);
248
EXPECT_EQ(2, o->ref_count);
249
// Now it dies and refcount is reduced.
252
EXPECT_EQ(1, o->ref_count);
256
TEST_F(GObjectMemoryTest, swap)
258
FooBar* o1 = foo_bar_new();
259
FooBar* o2 = foo_bar_new();
260
auto u1 = unique_gobject(o1);
261
auto u2 = unique_gobject(o2);
264
EXPECT_EQ(o2, u1.get());
265
EXPECT_EQ(o1, u2.get());
268
EXPECT_EQ(o1, u1.get());
269
EXPECT_EQ(o2, u2.get());
272
TEST_F(GObjectMemoryTest, floating)
274
GObject* o = G_OBJECT(g_object_new(G_TYPE_INITIALLY_UNOWNED, nullptr));
275
auto u = unique_gobject<GObject>(o);
276
// it should have sunk the reference
277
EXPECT_TRUE(g_object_is_floating(u.get()) == FALSE);
280
TEST_F(GObjectMemoryTest, move)
282
GObject* o1 = G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr));
283
GObject* o2 = G_OBJECT(g_object_new(G_TYPE_OBJECT, nullptr));
285
auto u1 = unique_gobject<GObject>(o1);
286
auto u2 = unique_gobject<GObject>(o2);
288
EXPECT_TRUE(u1.get() == o2);
290
EXPECT_TRUE(o1->ref_count == 1);
294
TEST_F(GObjectMemoryTest, null)
297
FooBar* o3 = foo_bar_new();
298
auto u1 = unique_gobject(o1);
299
auto u2 = unique_gobject<FooBar>(nullptr);
300
auto u3 = unique_gobject(o3);
308
TEST_F(GObjectMemoryTest, reset)
310
FooBar* o1 = foo_bar_new();
311
FooBar* o2 = foo_bar_new();
312
auto u = unique_gobject(o1);
315
EXPECT_EQ(o2, u.get());
320
TEST_F(GObjectMemoryTest, sizeoftest) {
321
EXPECT_EQ(sizeof(FooBar*), sizeof(unique_ptr<FooBar>));
324
TEST_F(GObjectMemoryTest, hash)
326
unordered_set<shared_ptr<FooBar>> s;
327
auto a = share_gobject(foo_bar_new());
328
auto b = share_gobject(foo_bar_new());
329
auto c = share_gobject(foo_bar_new());
332
EXPECT_EQ(1, s.size());
333
EXPECT_FALSE(s.end() == s.find(a));
336
EXPECT_EQ(2, s.size());
337
EXPECT_FALSE(s.end() == s.find(b));
340
EXPECT_EQ(3, s.size());
341
EXPECT_FALSE(s.end() == s.find(c));
343
// Shouldn't add the duplicates
347
EXPECT_EQ(3, s.size());
350
EXPECT_EQ(2, s.size());
353
EXPECT_EQ(1, s.size());
356
EXPECT_TRUE(s.empty());
359
TEST_F(GObjectMemoryTest, uniquePtrDeletesGObjects)
362
auto a = unique_gobject(foo_bar_new_full("a", 1));
363
auto b = unique_gobject(foo_bar_new_full("b", 2));
365
EXPECT_EQ(list<Deleted>({{"b", 2}, {"a", 1}}), DELETED_OBJECTS);
368
TEST_F(GObjectMemoryTest, sharedPtrDeletesGObjects)
371
auto a = share_gobject(foo_bar_new_full("a", 1));
372
auto b = share_gobject(foo_bar_new_full("b", 2));
374
EXPECT_EQ(list<Deleted>({{"b", 2}, {"a", 1}}), DELETED_OBJECTS);
377
TEST_F(GObjectMemoryTest, makeGObjectDeletesGObjects)
380
auto a = make_gobject<FooBar>(FOO_TYPE_BAR, "name", "a", "id", 1, nullptr);
381
auto b = make_gobject<FooBar>(FOO_TYPE_BAR, "name", "b", "id", 2, nullptr);
382
auto c = make_gobject<FooBar>(FOO_TYPE_BAR, "name", "c", "id", 3, nullptr);
384
EXPECT_EQ(list<Deleted>({{"c", 3}, {"b", 2}, {"a", 1}}), DELETED_OBJECTS);
387
TEST_F(GObjectMemoryTest, foo)
390
shared_ptr<FooBar> s;
391
auto u = unique_gobject(foo_bar_new_full("hi", 6));
393
// unique instance has been stolen from
396
EXPECT_EQ(list<Deleted>({{"hi", 6}}), DELETED_OBJECTS);
398
shared_ptr<FooBar> s(unique_gobject(foo_bar_new_full("bye", 7)));
400
EXPECT_EQ(list<Deleted>({{"hi", 6}, {"bye", 7}}), DELETED_OBJECTS);
403
typedef pair<const char*, guint> GObjectMemoryMakeSharedTestParam;
405
class GObjectMemoryMakeHelperMethodsTest: public testing::TestWithParam<GObjectMemoryMakeSharedTestParam>
409
* We test for multiple properties so that we can be sure the various
410
* helper methods correctly pass through different data types and
411
* multiple combinations of arguments correctly.
413
static void checkProperties(gpointer obj, const char* expectedName, guint expectedId)
418
g_object_get(obj, "name", &name, "id", &id, NULL);
419
EXPECT_STREQ(expectedName, name);
420
EXPECT_EQ(expectedId, id);
426
TEST_P(GObjectMemoryMakeHelperMethodsTest, make_gobject_passes_arguments)
429
auto obj = make_gobject<FooBar>(FOO_TYPE_BAR, "name", p.first, "id", p.second, nullptr);
430
checkProperties(obj.get(), p.first, p.second);
433
TEST_P(GObjectMemoryMakeHelperMethodsTest, make_gobject_no_arguments)
436
auto obj = make_gobject<FooBar>(FOO_TYPE_BAR, nullptr);
437
g_object_set(obj.get(), "name", p.first, "id", p.second, nullptr);
438
checkProperties(obj.get(), p.first, p.second);
441
TEST_P(GObjectMemoryMakeHelperMethodsTest, unique_foo_bar_new)
444
auto obj = unique_gobject(foo_bar_new_full(p.first,p.second));
445
checkProperties(obj.get(), p.first, p.second);
448
TEST_P(GObjectMemoryMakeHelperMethodsTest, share_foo_bar_new)
451
auto obj = share_gobject(foo_bar_new_full(p.first, p.second));
452
checkProperties(obj.get(), p.first, p.second);
455
INSTANTIATE_TEST_CASE_P(BunchOfNames,
456
GObjectMemoryMakeHelperMethodsTest,
457
::testing::Values(GObjectMemoryMakeSharedTestParam{"meeny", 1},
458
GObjectMemoryMakeSharedTestParam{"miny", 2},
459
GObjectMemoryMakeSharedTestParam{"moe", 3}));