/* Copyright (C) 2016 The giomm Development Team * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ // This test is similar to glib/gio/tests/glistmodel.c. #include #include #include #include namespace { int result = EXIT_SUCCESS; void check_store_boundaries_n_items(int icall, const Glib::RefPtr& store, unsigned int expected) { if (store->get_n_items() != expected) { result = EXIT_FAILURE; std::cerr << "test_store_boundaries(), " << icall << ": get_n_items()=" << store->get_n_items() << std::endl; } } void test_store_boundaries() { auto store = Gio::ListStore::create(); auto item = Gio::MenuItem::create("", ""); std::weak_ptr weakref_item = item; // Remove an item from an empty list. store->remove(0); check_store_boundaries_n_items(1, store, 0); // Don't allow inserting an item past the end ... store->insert(1, item); check_store_boundaries_n_items(2, store, 0); // ... except exactly at the end. store->insert(0, item); check_store_boundaries_n_items(3, store, 1); // Remove a non-existing item at exactly the end of the list. store->remove(1); check_store_boundaries_n_items(4, store, 1); // Remove an existing item. store->remove(0); check_store_boundaries_n_items(5, store, 0); // Splice beyond the end of the list. store->splice(1, 0, std::vector>()); check_store_boundaries_n_items(6, store, 0); // Remove items from an empty list. store->splice(0, 1, std::vector>()); check_store_boundaries_n_items(7, store, 0); // Append an item, remove it, and insert it by splicing. store->append(item); { std::vector> v; v.push_back(item); store->splice(0, 1, v); } check_store_boundaries_n_items(8, store, 1); // Remove more items than exist. store->splice(0, 5, std::vector>()); check_store_boundaries_n_items(9, store, 1); store.reset(); item.reset(); if (weakref_item.lock()) { result = EXIT_FAILURE; std::cerr << "test_store_boundaries(), 10: weakref_item is not null" << std::endl; } } // end test_store_boundaries() void check_store_refcounts_n_items(int icall, const Glib::RefPtr& store, unsigned int expected) { if (store->get_n_items() != expected) { result = EXIT_FAILURE; std::cerr << "test_store_refcounts(), " << icall << ": get_n_items()=" << store->get_n_items() << std::endl; } if (store->get_object(expected)) { result = EXIT_FAILURE; std::cerr << "test_store_refcounts(), " << icall << ": get_object(" << expected << ") is not null" << std::endl; } } void test_store_refcounts() { auto store = Gio::ListStore::create(); check_store_refcounts_n_items(1, store, 0); const std::size_t n_items = 10; std::vector> items; std::vector> weakref_items; for (std::size_t i = 0; i < n_items; ++i) { items.push_back(Gio::MenuItem::create("", "")); weakref_items.emplace_back(items[i]); store->append(items[i]); } check_store_refcounts_n_items(2, store, n_items); if (store->get_item(3).get() != items[3].get()) { result = EXIT_FAILURE; std::cerr << "test_store_refcounts(), 3: get_item(3) != items[3]" << std::endl; } for (std::size_t i = 0; i < n_items; ++i) { items[i].reset(); } store->remove(4); check_store_refcounts_n_items(6, store, n_items-1); store.reset(); for (std::size_t i = 0; i < n_items; ++i) { if (weakref_items[i].lock()) { result = EXIT_FAILURE; std::cerr << "test_store_refcounts(), 7: weakref_items[" << i << "] is not null" << std::endl; } } } // end test_store_refcounts() // All returned numbers are different as long as the number of calls are < 15000. gint32 get_next_number() { static gint32 n_calls = 0; ++n_calls; const gint32 n1 = n_calls; const gint32 n2 = 30000 - n_calls; gint32 res = (n2 << 16) | n1; if (n_calls & 1) res = (n1 << 16) | n2; return res; } int compare_items1(const Glib::RefPtr& a, const Glib::RefPtr& b) { const auto action_a = std::dynamic_pointer_cast(a); const auto action_b = std::dynamic_pointer_cast(b); if (!action_a || !action_b) { result = EXIT_FAILURE; std::cerr << "compare_items1(): cast_dynamic() failed" << std::endl; return 0; } gint32 value_a = 0; gint32 value_b = 0; action_a->get_state(value_a); action_b->get_state(value_b); return value_a - value_b; } void insert_item_sorted1(const Glib::RefPtr>& store, gint32 n) { auto obj = Gio::SimpleAction::create_radio_integer("dummy", n); store->insert_sorted(obj, sigc::ptr_fun(compare_items1)); } void test_store_sorted1() { // Test that a subclass of Glib::Object can be stored in and retrieved from // a Gio::ListStore. auto store = Gio::ListStore::create(); const std::size_t n_items2 = 100; // n_items2*2 items are stored. for (std::size_t i = 0; i < n_items2; ++i) { const auto n = get_next_number(); insert_item_sorted1(store, n); insert_item_sorted1(store, n); // Multiple copies of the same are OK } if (store->get_n_items() != n_items2*2) { result = EXIT_FAILURE; std::cerr << "test_store_sorted1(), 1: get_n_items()=" << store->get_n_items() << std::endl; } for (std::size_t i = 0; i < n_items2; ++i) { // Should see our two copies. auto a = store->get_item(i * 2); auto b = store->get_item(i * 2 + 1); if (compare_items1(a, b) != 0) { result = EXIT_FAILURE; std::cerr << "test_store_sorted1(), 2: i=" << i << ", items are not equal" << std::endl; } if (a.get() == b.get()) { result = EXIT_FAILURE; std::cerr << "test_store_sorted1(), 3: i=" << i << ", items are the same" << std::endl; } if (i > 0) { auto c = store->get_item(i * 2 - 1); if (c.get() == a.get() || c.get() == b.get()) { result = EXIT_FAILURE; std::cerr << "test_store_sorted1(), 4: i=" << i << ", items are the same" << std::endl; } if (!(compare_items1(a, c) > 0 && compare_items1(b, c) > 0)) { result = EXIT_FAILURE; std::cerr << "test_store_sorted1(), 5: i=" << i << ", c is not less than a and b" << std::endl; } } } } // end test_store_sorted1() // User-defined class class MyObject : public Glib::Object { protected: explicit MyObject(int id) : m_id(id) {} public: static Glib::RefPtr create(int id) { return Glib::make_refptr_for_instance(new MyObject(id)); } int get_id() const { return m_id; } static int compare(const Glib::RefPtr& a, const Glib::RefPtr& b) { if (!a || !b) { result = EXIT_FAILURE; std::cerr << "MyObject::compare(): Empty RefPtr" << std::endl; return 0; } return a->get_id() - b->get_id(); } private: int m_id; }; void test_store_sorted2() { // Test that a user-defined class, derived from Glib::Object, can be stored in // and retrieved from a Gio::ListStore<>. auto store = Gio::ListStore::create(); const std::size_t n_items2 = 100; // n_items2*2 items are stored. for (std::size_t i = 0; i < n_items2; ++i) { const auto n = get_next_number(); // Multiple copies of the same are OK store->insert_sorted(MyObject::create(n), sigc::ptr_fun(&MyObject::compare)); store->insert_sorted(MyObject::create(n), sigc::ptr_fun(&MyObject::compare)); } if (store->get_n_items() != n_items2*2) { result = EXIT_FAILURE; std::cerr << "test_store_sorted2(), 1: get_n_items()=" << store->get_n_items() << std::endl; } for (std::size_t i = 0; i < n_items2; ++i) { // Should see our two copies. auto a = store->get_item(i * 2); auto b = store->get_item(i * 2 + 1); if (MyObject::compare(a, b) != 0) { result = EXIT_FAILURE; std::cerr << "test_store_sorted2(), 2: i=" << i << ", items are not equal" << std::endl; } if (a.get() == b.get()) { result = EXIT_FAILURE; std::cerr << "test_store_sorted2(), 3: i=" << i << ", items are the same" << std::endl; } if (i > 0) { auto c = store->get_item(i * 2 - 1); if (c.get() == a.get() || c.get() == b.get()) { result = EXIT_FAILURE; std::cerr << "test_store_sorted2(), 4: i=" << i << ", items are the same" << std::endl; } if (!(MyObject::compare(a, c) > 0 && MyObject::compare(b, c) > 0)) { result = EXIT_FAILURE; std::cerr << "test_store_sorted2(), 5: i=" << i << ", c is not less than a and b" << std::endl; } } } } // end test_store_sorted2() void check_found_item_position(int icall, bool found_item, unsigned int position, bool expected_found_item, unsigned int expected_position) { if (found_item != expected_found_item || position != expected_position) { result = EXIT_FAILURE; std::cerr << "test_store_find(), " << icall << ": found_item=" << found_item << ", position=" << position << std::endl; } } bool casecmp_action_by_name(const Glib::RefPtr& a, const Glib::RefPtr& b, const Glib::ustring& suffix) { const auto a_name = a->get_name(); const auto b_name = b->get_name() + suffix; if (suffix.empty()) return a_name.casefold() == b_name.casefold(); else return a_name == b_name; } void test_store_find() { const std::vector item_strings{ "aaa", "bbb", "xxx", "ccc" }; std::vector> items; for (auto& item_string : item_strings) items.push_back(Gio::SimpleAction::create(item_string)); auto store = Gio::ListStore::create(); // Shouldn't crash on an empty list. auto [found_item, position] = store->find(items[0]); check_found_item_position(1, found_item, position, false, std::numeric_limits::max()); for (auto& item : items) store->append(item); // Check whether it can find the elements. for (unsigned int i = 0; i < item_strings.size(); ++i) { std::tie(found_item, position) = store->find(items[i]); check_found_item_position(2+i, found_item, position, true, i); } // Try to find element not part of the list. auto other_item = Gio::SimpleAction::create("111"); std::tie(found_item, position) = store->find(other_item); check_found_item_position(6, found_item, position, false, std::numeric_limits::max()); // Re-add item; find() should only return the first position. store->append(items[0]); std::tie(found_item, position) = store->find(items[0]); check_found_item_position(7, found_item, position, true, 0); // Try to find element which should only work with custom equality check. other_item = Gio::SimpleAction::create("XXX"); std::tie(found_item, position) = store->find(other_item, sigc::bind(sigc::ptr_fun(casecmp_action_by_name), "")); check_found_item_position(8, found_item, position, true, 2); other_item = Gio::SimpleAction::create("c"); std::tie(found_item, position) = store->find(other_item, sigc::bind(sigc::ptr_fun(casecmp_action_by_name), "cc")); check_found_item_position(8, found_item, position, true, 3); } // end test_store_find() } // anonymous namespace int main(int, char**) { Gio::init(); test_store_boundaries(); test_store_refcounts(); test_store_sorted1(); test_store_sorted2(); test_store_find(); return result; }