@@ -0,0 +1,484 @@
2+* Copyright (C) 2013 Canonical, Ltd.
5+* Pawel Stolowski <pawel.stolowski@canonical.com>
7+* This program is free software; you can redistribute it and/or modify
8+* it under the terms of the GNU General Public License as published by
9+* the Free Software Foundation; version 3.
11+* This program is distributed in the hope that it will be useful,
12+* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+* GNU General Public License for more details.
16+* You should have received a copy of the GNU General Public License
17+* along with this program. If not, see <http://www.gnu.org/licenses/>.
20+#include "filtersbindingstest.h"
23+#include <UnityCore/Filter.h>
24+#include <UnityCore/MultiRangeFilter.h>
35+#include "multirangefilter.h"
36+#include "checkoptionfilter.h"
37+#include "radiooptionfilter.h"
38+#include "ratingsfilter.h"
39+#include "ratingfilteroption.h"
40+#include "genericoptionsmodel.h"
42+void FiltersBindingsTest::initTestCase()
46+GVariant* FiltersBindingsTest::createOptions(int numOfOptions)
48+ auto vtype = g_variant_type_new("a(sssb)");
49+ auto vb = g_variant_builder_new(vtype);
50+ for (int i = 0; i<numOfOptions; i++) {
51+ const std::string optId = "opt" + std::to_string(i);
52+ const std::string optName = "Option " + std::to_string(i);
53+ g_variant_builder_add(vb, "(sssb)", optId.c_str(), optName.c_str(), "", false);
55+ return g_variant_builder_end(vb);
58+DeeModel* FiltersBindingsTest::createFilterModel()
60+ const char* filter_schema[8] = {"s", "s", "s", "s", "a{sv}", "b", "b", "b"};
61+ auto model = dee_sequence_model_new();
62+ dee_model_set_schema_full (DEE_MODEL(model), filter_schema, 8);
66+void FiltersBindingsTest::createMultiRangeFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
68+ createFilter(model, "filter-multirange", id, name, optionCount);
71+void FiltersBindingsTest::createRadioOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
73+ createFilter(model, "filter-radiooption", id, name, optionCount);
76+void FiltersBindingsTest::createCheckOptionFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
78+ createFilter(model, "filter-checkoption", id, name, optionCount);
81+void FiltersBindingsTest::createRatingsFilter(DeeModel *model, const std::string &id, const std::string &name)
85+ row[0] = g_variant_new_string(id.c_str());
86+ row[1] = g_variant_new_string(name.c_str());
87+ row[2] = g_variant_new_string(""); // icon hint
88+ row[3] = g_variant_new_string("filter-ratings");
90+ GVariant *children[2];
91+ GVariant *key1 = g_variant_new_string("show-all-button");
92+ GVariant *key2 = g_variant_new_string("rating");
93+ children[0] = g_variant_new_dict_entry(key1, g_variant_new_variant(g_variant_new_boolean(false)));
94+ children[1] = g_variant_new_dict_entry(key2, g_variant_new_variant(g_variant_new_double(0.0f)));
95+ auto fhints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, 2);
98+ row[5] = g_variant_new_boolean(true); // visible
99+ row[6] = g_variant_new_boolean(false); // collapsed
100+ row[7] = g_variant_new_boolean(false); // filtering
102+ dee_model_append_row(model, row);
105+void FiltersBindingsTest::createFilter(DeeModel *model, const std::string &renderer, const std::string &id, const std::string &name, int optionCount)
109+ row[0] = g_variant_new_string(id.c_str());
110+ row[1] = g_variant_new_string(name.c_str());
111+ row[2] = g_variant_new_string(""); // icon hint
112+ row[3] = g_variant_new_string(renderer.c_str()); // renderer
114+ GVariant *children[2];
115+ GVariant *key1 = g_variant_new_string("show-all-button");
116+ GVariant *key2 = g_variant_new_string("options");
117+ children[0] = g_variant_new_dict_entry(key1, g_variant_new_variant(g_variant_new_boolean(false)));
118+ children[1] = g_variant_new_dict_entry(key2, g_variant_new_variant(createOptions(optionCount)));
119+ auto fhints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, 2);
122+ row[5] = g_variant_new_boolean(true); // visible
123+ row[6] = g_variant_new_boolean(false); // collapsed
124+ row[7] = g_variant_new_boolean(false); // filtering
126+ dee_model_append_row(model, row);
129+void FiltersBindingsTest::testMultiRangeFilter()
131+ auto model = createFilterModel();
133+ createMultiRangeFilter(model, "f1", "Filter1", 1);
134+ createMultiRangeFilter(model, "f2", "Filter2", 2);
135+ createMultiRangeFilter(model, "f3", "Filter3", 3);
137+ // create filter out of 1st row
138+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
140+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
141+ QVERIFY(core_filter != nullptr);
142+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
143+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
144+ QVERIFY(multi_filter != nullptr);
145+ auto options = multi_filter->options();
146+ QCOMPARE(options->rowCount(), 1);
148+ auto idx = options->index(0);
149+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
150+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
151+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
152+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
154+ QCOMPARE(id_var.toString(), QString("opt0"));
155+ QCOMPARE(name_var.toString(), QString("Option 0"));
156+ QCOMPARE(icon_var.toString(), QString(""));
157+ QCOMPARE(active_var.toBool(), false);
159+ delete multi_filter;
162+ // create filter out of 2nd row
163+ iter = dee_model_next(DEE_MODEL(model), iter);
165+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
166+ QVERIFY(core_filter != nullptr);
167+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
168+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
169+ QVERIFY(multi_filter != nullptr);
170+ auto options = multi_filter->options();
171+ QCOMPARE(options->rowCount(), 2);
173+ auto idx = options->index(0);
174+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
175+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
176+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
177+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
179+ QCOMPARE(id_var.toString(), QString("opt0"));
180+ QCOMPARE(name_var.toString(), QString("Option 0"));
181+ QCOMPARE(icon_var.toString(), QString(""));
182+ QCOMPARE(active_var.toBool(), false);
184+ AbstractFilterOption* opt1 = options->getRawOption(0);
185+ QCOMPARE(opt1->id(), QString("opt0"));
187+ AbstractFilterOption* opt2 = options->getRawOption(1);
188+ QCOMPARE(opt2->id(), QString("opt1"));
190+ delete multi_filter;
193+ // create filter out of 3nd row
194+ iter = dee_model_next(DEE_MODEL(model), iter);
196+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
197+ QVERIFY(core_filter != nullptr);
198+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
199+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
200+ QVERIFY(multi_filter != nullptr);
201+ auto options = multi_filter->options();
202+ QCOMPARE(options->rowCount(), 3);
204+ AbstractFilterOption* opt0 = options->getRawOption(0);
205+ QCOMPARE(opt0->id(), QString("opt0"));
207+ AbstractFilterOption* opt1 = options->getRawOption(1);
208+ QCOMPARE(opt1->id(), QString("opt1"));
210+ AbstractFilterOption* opt2 = options->getRawOption(2);
211+ QCOMPARE(opt2->id(), QString("opt2"));
213+ // verify combined filter options are bound to correct underlying unity options
214+ auto core_multi_filter = std::dynamic_pointer_cast<unity::dash::MultiRangeFilter>(core_filter);
215+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[0]->id()), QString("opt0"));
216+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[1]->id()), QString("opt1"));
217+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[2]->id()), QString("opt2"));
219+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
220+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
221+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
223+ // test active property changes
224+ QCOMPARE(opt0->active(), false);
226+ opt0->setActive(true); // activate 1st option
227+ QCOMPARE(opt0spy.count(), 1);
228+ QCOMPARE(opt1spy.count(), 0);
229+ QCOMPARE(opt2spy.count(), 0);
230+ QCOMPARE(opt0->active(), true);
231+ QCOMPARE(opt1->active(), false);
232+ QCOMPARE(opt2->active(), false);
234+ opt1->setActive(true);
235+ QCOMPARE(opt0spy.count(), 2);
236+ QCOMPARE(opt1spy.count(), 1);
237+ QCOMPARE(opt2spy.count(), 0);
238+ QCOMPARE(opt0->active(), false); //1st combined option gets deactivated
239+ QCOMPARE(opt1->active(), true);
240+ QCOMPARE(opt2->active(), false);
242+ opt1->setActive(false); // and de-activate it
243+ QCOMPARE(opt0spy.count(), 2);
244+ QCOMPARE(opt1spy.count(), 2);
245+ QCOMPARE(opt2spy.count(), 0);
246+ QCOMPARE(opt0->active(), false);
247+ QCOMPARE(opt1->active(), false);
248+ QCOMPARE(opt2->active(), false);
250+ opt2->setActive(true); // activate another combined option
251+ QCOMPARE(opt0spy.count(), 2);
252+ QCOMPARE(opt1spy.count(), 2);
253+ QCOMPARE(opt2spy.count(), 1);
254+ QCOMPARE(opt0->active(), false);
255+ QCOMPARE(opt1->active(), false);
256+ QCOMPARE(opt2->active(), true);
258+ delete multi_filter;
262+void FiltersBindingsTest::testCheckOptionFilter()
264+ auto model = createFilterModel();
266+ createCheckOptionFilter(model, "f1", "Filter1", 3);
268+ // create filter out of 1st row
269+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
271+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
272+ QVERIFY(core_filter != nullptr);
273+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
274+ auto check_filter = dynamic_cast<CheckOptionFilter*>(bind_filter);
275+ QVERIFY(check_filter != nullptr);
276+ auto options = check_filter->options();
277+ QCOMPARE(options->rowCount(), 3);
279+ QCOMPARE(check_filter->id(), QString("f1"));
280+ QCOMPARE(check_filter->name(), QString("Filter1"));
281+ QCOMPARE(check_filter->iconHint(), QString(""));
282+ QCOMPARE(check_filter->rendererName(), QString("filter-checkoption"));
283+ QCOMPARE(check_filter->visible(), true);
284+ QCOMPARE(check_filter->collapsed(), false);
285+ QCOMPARE(check_filter->filtering(), false);
287+ auto idx = options->index(0);
288+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
289+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
290+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
291+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
293+ QCOMPARE(id_var.toString(), QString("opt0"));
294+ QCOMPARE(name_var.toString(), QString("Option 0"));
295+ QCOMPARE(icon_var.toString(), QString(""));
296+ QCOMPARE(active_var.toBool(), false);
298+ idx = options->index(1);
299+ id_var = options->data(idx, GenericOptionsModel::RoleId);
300+ name_var = options->data(idx, GenericOptionsModel::RoleName);
301+ icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
302+ active_var = options->data(idx, GenericOptionsModel::RoleActive);
304+ QCOMPARE(id_var.toString(), QString("opt1"));
305+ QCOMPARE(name_var.toString(), QString("Option 1"));
306+ QCOMPARE(icon_var.toString(), QString(""));
307+ QCOMPARE(active_var.toBool(), false);
309+ AbstractFilterOption* opt0 = options->getRawOption(0);
310+ AbstractFilterOption* opt1 = options->getRawOption(1);
311+ AbstractFilterOption* opt2 = options->getRawOption(2);
313+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
314+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
315+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
316+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
318+ // test active property changes
319+ QCOMPARE(opt0->active(), false);
320+ QCOMPARE(opt1->active(), false);
321+ QCOMPARE(opt2->active(), false);
323+ opt0->setActive(true);
324+ QCOMPARE(opt0spy.count(), 1);
325+ QCOMPARE(opt1spy.count(), 0);
326+ QCOMPARE(opt2spy.count(), 0);
327+ QCOMPARE(opt0->active(), true);
328+ QCOMPARE(opt1->active(), false);
329+ QCOMPARE(opt2->active(), false);
331+ opt1->setActive(true);
332+ QCOMPARE(opt0spy.count(), 2);
333+ QCOMPARE(opt1spy.count(), 1);
334+ QCOMPARE(opt2spy.count(), 0);
335+ QCOMPARE(opt0->active(), false);
336+ QCOMPARE(opt1->active(), true);
337+ QCOMPARE(opt2->active(), false);
339+ QCOMPARE(dataChangedSpy.count(), 3);
341+ delete check_filter;
345+void FiltersBindingsTest::testRadioOptionFilter()
347+ auto model = createFilterModel();
349+ createRadioOptionFilter(model, "f1", "Filter1", 3);
351+ // create filter out of 1st row
352+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
354+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
355+ QVERIFY(core_filter != nullptr);
356+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
357+ auto radio_filter = dynamic_cast<RadioOptionFilter*>(bind_filter);
358+ QVERIFY(radio_filter != nullptr);
359+ auto options = radio_filter->options();
360+ QCOMPARE(options->rowCount(), 3);
362+ auto idx = options->index(0);
363+ QVariant id_var = options->data(idx, GenericOptionsModel::RoleId);
364+ QVariant name_var = options->data(idx, GenericOptionsModel::RoleName);
365+ QVariant icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
366+ QVariant active_var = options->data(idx, GenericOptionsModel::RoleActive);
368+ QCOMPARE(id_var.toString(), QString("opt0"));
369+ QCOMPARE(name_var.toString(), QString("Option 0"));
370+ QCOMPARE(icon_var.toString(), QString(""));
371+ QCOMPARE(active_var.toBool(), false);
373+ idx = options->index(1);
374+ id_var = options->data(idx, GenericOptionsModel::RoleId);
375+ name_var = options->data(idx, GenericOptionsModel::RoleName);
376+ icon_var = options->data(idx, GenericOptionsModel::RoleIconHint);
377+ active_var = options->data(idx, GenericOptionsModel::RoleActive);
379+ QCOMPARE(id_var.toString(), QString("opt1"));
380+ QCOMPARE(name_var.toString(), QString("Option 1"));
381+ QCOMPARE(icon_var.toString(), QString(""));
382+ QCOMPARE(active_var.toBool(), false);
384+ AbstractFilterOption* opt0 = options->getRawOption(0);
385+ AbstractFilterOption* opt1 = options->getRawOption(1);
386+ AbstractFilterOption* opt2 = options->getRawOption(2);
388+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
389+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
390+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
391+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
393+ // test active property changes
394+ QCOMPARE(opt0->active(), false);
395+ QCOMPARE(opt1->active(), false);
396+ QCOMPARE(opt2->active(), false);
398+ opt0->setActive(true);
399+ QCOMPARE(opt0spy.count(), 1);
400+ QCOMPARE(opt1spy.count(), 0);
401+ QCOMPARE(opt2spy.count(), 0);
402+ QCOMPARE(opt0->active(), true);
403+ QCOMPARE(opt1->active(), false);
404+ QCOMPARE(opt2->active(), false);
406+ opt1->setActive(true);
407+ QCOMPARE(opt0spy.count(), 2);
408+ QCOMPARE(opt1spy.count(), 1);
409+ QCOMPARE(opt2spy.count(), 0);
410+ QCOMPARE(opt0->active(), false);
411+ QCOMPARE(opt1->active(), true);
412+ QCOMPARE(opt2->active(), false);
414+ QCOMPARE(dataChangedSpy.count(), 3);
416+ delete radio_filter;
420+void FiltersBindingsTest::testRatingsFilter()
422+ auto model = createFilterModel();
424+ createRatingsFilter(model, "f1", "Filter1");
426+ // create filter out of 1st row
427+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
429+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
430+ QVERIFY(core_filter != nullptr);
431+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
432+ auto rating_filter = dynamic_cast<RatingsFilter*>(bind_filter);
433+ QVERIFY(rating_filter != nullptr);
434+ auto options = rating_filter->options();
435+ QCOMPARE(options->rowCount(), 5);
437+ RatingFilterOption* opt[5];
438+ for (int i = 0; i<5; i++) {
439+ opt[i] = dynamic_cast<RatingFilterOption *>(options->getRawOption(i));
442+ QSignalSpy opt0spy(opt[0], SIGNAL(activeChanged(bool)));
443+ QSignalSpy opt4spy(opt[4], SIGNAL(activeChanged(bool)));
444+ QSignalSpy dataChangedSpy(options, SIGNAL(dataChanged(const QModelIndex&, const QModelIndex&, const QVector<int>&)));
446+ QVERIFY(qAbs(opt[0]->value() - 0.2f) <= 0.0001f);
447+ QVERIFY(qAbs(opt[1]->value() - 0.4f) <= 0.0001f);
448+ QVERIFY(qAbs(opt[2]->value() - 0.6f) <= 0.0001f);
449+ QVERIFY(qAbs(opt[3]->value() - 0.8f) <= 0.0001f);
450+ QVERIFY(qAbs(opt[4]->value() - 1.0f) <= 0.0001f);
452+ QVERIFY(rating_filter->rating() <= 0.0001f); // rating equals zero
454+ // all options initially inactive
455+ for (int i=0; i<5; i++) {
456+ QCOMPARE(opt[i]->active(), false);
459+ // verify activation for all options
460+ for (int i=0; i<5; i++) {
461+ opt[i]->setActive(true);
462+ QCOMPARE(opt[i]->active(), true);
463+ QVERIFY(rating_filter->rating() - ((i+1)*0.2f) <= 0.0001f);
465+ // verify that all other options are inactive
466+ for (int j=0; j<5; j++) {
468+ QCOMPARE(opt[j]->active(), false);
473+ QCOMPARE(opt0spy.count(), 2);
474+ QCOMPARE(opt4spy.count(), 1);
476+ opt[4]->setActive(false);
477+ QVERIFY(rating_filter->rating() <= 0.0001f); // rating equals zero
478+ QCOMPARE(opt4spy.count(), 2);
480+ QCOMPARE(dataChangedSpy.count(), 10);
484+QTEST_MAIN(FiltersBindingsTest)