on xenial help sign in
Added tests for multi range filter.
commitc2d862725fc02b521dce92e02478d35606f010ae authorPawel Stolowski <pawel.stolowski@canonical.com> dateMon 8 Jul 2013, 18:37 (13 years ago) parent925013e
9 files changed+328−26
plugins/Unity/combinedfilteroption.cpp+34−9 plugins/Unity/combinedfilteroption.h+18−2 plugins/Unity/combinedfilteroptions.cpp+22−12 plugins/Unity/combinedfilteroptions.h+2 plugins/Unity/filter.h+1−1 plugins/Unity/multirangefilter.h+1−1 tests/plugins/Unity/CMakeLists.txt+5−1 tests/plugins/Unity/filtersbindingstest.cpp+214 tests/plugins/Unity/filtersbindingstest.h+31
plugins/Unity/combinedfilteroption.cpp+34−9
@@ -18,10 +18,12 @@
1818 */
1919
2020#include "combinedfilteroption.h"
21+#include <QDebug>
2122
2223CombinedFilterOption::CombinedFilterOption(unity::dash::FilterOption::Ptr unityFilterOption1, unity::dash::FilterOption::Ptr unityFilterOption2, QObject *parent)
2324 : AbstractFilterOption(parent),
2425 m_active(false),
26+ m_requested_active(false),
2527 m_unityFilterOption {NULL, NULL}
2628{
2729 setUnityFilterOption(unityFilterOption1, unityFilterOption2);
@@ -59,18 +61,35 @@ QString CombinedFilterOption::iconHint() const
5961bool CombinedFilterOption::active() const
6062{
6163 if (m_unityFilterOption[1] != NULL)
62− return m_unityFilterOption[0]->active && m_unityFilterOption[1]->active;
63− return m_unityFilterOption[0]->active;
64+ return m_unityFilterOption[0]->active && m_unityFilterOption[1]->active && m_requested_active;
65+ return m_unityFilterOption[0]->active && m_requested_active;
6466}
6567
6668void CombinedFilterOption::setActive(bool active)
6769{
68− m_active = active; //???
69− m_unityFilterOption[0]->active;
70+ m_requested_active = active;
71+ m_unityFilterOption[0]->active = active;
7072 if (m_unityFilterOption[1] != NULL)
7173 m_unityFilterOption[1]->active = active;
7274}
7375
76+void CombinedFilterOption::setInactive(const CombinedFilterOption &otherFilter)
77+{
78+ // de-activate underlying unity filter options as long as they don't belong
79+ // to otherFilter.
80+ if (this != &otherFilter) {
81+ for (int i = 0; i<2; i++) {
82+ if (m_unityFilterOption[i] != nullptr) {
83+ if (m_unityFilterOption[i]->active &&
84+ m_unityFilterOption[i] != otherFilter.m_unityFilterOption[0] &&
85+ m_unityFilterOption[i] != otherFilter.m_unityFilterOption[1]) {
86+ m_unityFilterOption[i]->active = false;
87+ }
88+ }
89+ }
90+ }
91+}
92+
7493void CombinedFilterOption::onIdChanged(const std::string &/* id */)
7594{
7695 Q_EMIT idChanged(getCombinedId());
@@ -78,7 +97,11 @@ void CombinedFilterOption::onIdChanged(const std::string &/* id */)
7897
7998void CombinedFilterOption::onActiveChanged(bool /*active*/)
8099{
81− Q_EMIT activeChanged(CombinedFilterOption::active());
100+ bool combinedState = CombinedFilterOption::active();
101+ if (m_active != combinedState) {
102+ m_active = combinedState;
103+ Q_EMIT activeChanged(m_active);
104+ }
82105}
83106
84107void CombinedFilterOption::setUnityFilterOption(unity::dash::FilterOption::Ptr unityFilterOption1, unity::dash::FilterOption::Ptr unityFilterOption2)
@@ -94,9 +117,11 @@ void CombinedFilterOption::setUnityFilterOption(unity::dash::FilterOption::Ptr u
94117
95118 /* Property change signals */
96119 for (int i=0; i<2; i++) {
97− m_unityFilterOption[i]->id.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::onIdChanged));
98− m_unityFilterOption[i]->name.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::nameChanged));
99− m_unityFilterOption[i]->icon_hint.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::iconHintChanged));
100− m_unityFilterOption[i]->active.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::onActiveChanged));
120+ if (m_unityFilterOption[i] != nullptr) {
121+ m_unityFilterOption[i]->id.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::onIdChanged));
122+ m_unityFilterOption[i]->name.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::nameChanged));
123+ m_unityFilterOption[i]->icon_hint.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::iconHintChanged));
124+ m_unityFilterOption[i]->active.changed.connect(sigc::mem_fun(this, &CombinedFilterOption::onActiveChanged));
125+ }
101126 }
102127}
plugins/Unity/combinedfilteroption.h+18−2
@@ -26,8 +26,14 @@
2626// libunity-core
2727#include <UnityCore/Filter.h>
2828
29−class CombinedFilterOption : public AbstractFilterOption
29+/**
30+ * Combines one or two Unity's filter options into one.
31+ * Becomes active if both of them are active.
32+ */
33+class Q_DECL_EXPORT CombinedFilterOption : public AbstractFilterOption
3034{
35+ Q_OBJECT
36+
3137public:
3238 explicit CombinedFilterOption(unity::dash::FilterOption::Ptr unityFilterOption1, unity::dash::FilterOption::Ptr unityFilterOption2 = NULL, QObject *parent = 0);
3339
@@ -38,7 +44,7 @@ public:
3844 bool active() const override;
3945
4046 /* setters */
41− void setActive(bool active) override;
47+ Q_INVOKABLE void setActive(bool active) override;
4248
4349private Q_SLOTS:
4450 void onIdChanged(const std::string &id);
@@ -49,8 +55,18 @@ private:
4955 std::string getCombinedName() const;
5056 void setUnityFilterOption(unity::dash::FilterOption::Ptr unityFilterOption1, unity::dash::FilterOption::Ptr unityFilterOption2 = NULL);
5157
58+ /* De-activate the filter, and also de-activate one or both of the underlying unity's filter options, depending on whether
59+ one of them is active in otherFilter. This is used internally by CombinedFilterOptions to ensure only one
60+ CombinedFilterOption is active. */
61+ void setInactive(const CombinedFilterOption &otherFilter);
62+
5263 bool m_active; //not needed???
64+ bool m_requested_active;
5365 unity::dash::FilterOption::Ptr m_unityFilterOption[2];
66+
67+ friend class CombinedFilterOptions;
5468};
5569
70+Q_DECLARE_METATYPE(CombinedFilterOption*)
71+
5672#endif /* COMBINEDFILTEROPTION_H */
plugins/Unity/combinedfilteroptions.cpp+22−12
@@ -21,10 +21,11 @@
2121
2222CombinedFilterOptions::CombinedFilterOptions(const std::vector<unity::dash::FilterOption::Ptr>& list,
2323 sigc::signal<void, unity::dash::FilterOption::Ptr> itemAddedSignal,
24− sigc::signal<void, unity::dash::FilterOption::Ptr> itemRemovedSignal,
24+ sigc::signal<void, unity::dash::FilterOption::Ptr> itemRemovedSignal,
2525 QObject *parent)
2626 : QAbstractListModel(parent)
2727{
28+ initList(list, itemAddedSignal, itemRemovedSignal);
2829}
2930
3031
@@ -49,22 +50,22 @@ void CombinedFilterOptions::initList(const std::vector<unity::dash::FilterOption
4950{
5051 // combine options, e.g. A, B, C becomes A-B, B-C, C
5152 unsigned int i = 0;
52− while (i < list.size()) {
53+ while (i < list.size() - 1) {
5354 unity::dash::FilterOption::Ptr option1 = list[i];
5455 unity::dash::FilterOption::Ptr option2 = NULL;
55− if (i < list.size()-1) {
56− option2 = list[i+1];
57− }
58− auto co = new CombinedFilterOption(option1);
56+ //if (i < list.size()-1) {
57+ option2 = list[i+1];
58+ //}
59+ auto co = new CombinedFilterOption(option1, option2);
5960 connect(co, SIGNAL(activeChanged(bool)), this, SLOT(onActiveChanged(bool)));
6061 addOption(co);
6162 ++i;
6263 }
63− if (list.size() > 1) {
64− auto co = new CombinedFilterOption(list[i-1], NULL);
64+ if (list.size() > 0) {
65+ auto co = new CombinedFilterOption(list[i], NULL);
6566 addOption(co);
6667 }
67−
68+
6869 itemAddedSignal.connect(sigc::mem_fun(this, &CombinedFilterOptions::onItemAdded));
6970 itemRemovedSignal.connect(sigc::mem_fun(this, &CombinedFilterOptions::onItemRemoved));
7071}
@@ -79,9 +80,18 @@ void CombinedFilterOptions::addOption(CombinedFilterOption *option)
7980
8081void CombinedFilterOptions::onActiveChanged(bool state)
8182{
82− CombinedFilterOption *option = dynamic_cast<CombinedFilterOption*>(QObject::sender());
83− if (option) {
84− //TODO: de-activate all others?
83+ // if option became active, need to disable all other options
84+ if (state) {
85+ CombinedFilterOption *option = dynamic_cast<CombinedFilterOption*>(QObject::sender());
86+ if (option) {
87+ Q_FOREACH (auto fo, m_list) {
88+ if (option != fo) {
89+ // note that changing state will result in onActiveChanged signal for that option, but
90+ // since it will be 'false', we won't end up in an infinite loop
91+ fo->setInactive(*option);
92+ }
93+ }
94+ }
8595 }
8696}
8797
plugins/Unity/combinedfilteroptions.h+2
@@ -32,6 +32,8 @@
3232
3333class CombinedFilterOptions : public QAbstractListModel
3434{
35+ Q_OBJECT
36+
3537public:
3638 CombinedFilterOptions(const std::vector<unity::dash::FilterOption::Ptr>& list,
3739 sigc::signal<void, unity::dash::FilterOption::Ptr> itemAddedSignal,
plugins/Unity/filter.h+1−1
@@ -27,7 +27,7 @@
2727// libunity-core
2828#include <UnityCore/Filter.h>
2929
30−class Filter : public QObject
30+class Q_DECL_EXPORT Filter : public QObject
3131{
3232 Q_OBJECT
3333
plugins/Unity/multirangefilter.h+1−1
@@ -32,7 +32,7 @@
3232#include "filteroption.h"
3333#include "combinedfilteroptions.h"
3434
35−class MultiRangeFilter : public Filter
35+class Q_DECL_EXPORT MultiRangeFilter : public Filter
3636{
3737 Q_OBJECT
3838
tests/plugins/Unity/CMakeLists.txt+5−1
@@ -1,11 +1,13 @@
11pkg_check_modules(UNITYCORE REQUIRED unity-core-6.0)
22pkg_check_modules(LIBUNITYPROTO REQUIRED unity-protocol-private)
3+pkg_check_modules(LIBUNITY REQUIRED unity)
34
45include_directories(
56 ${Unity-qml_SOURCE_DIR}
67 ${CMAKE_CURRENT_SOURCE_DIR}/../../../plugins/Unity
78 ${CMAKE_CURRENT_BINARY_DIR}
89 ${LIBUNITYPROTO_INCLUDE_DIRS}
10+ ${LIBUNITY_INCLUDE_DIRS}
911 ${UNITYCORE_INCLUDE_DIRS}
1012 )
1113
@@ -18,7 +20,7 @@ macro(run_tests)
1820 -o -,txt)
1921 add_test(NAME test${CLASSNAME}${_test} COMMAND ${testCommand})
2022 add_custom_target(${_test} ${testCommand})
21− add_executable(${_test}Exec ${_test}.cpp previewbindingstest.cpp)
23+ add_executable(${_test}Exec ${_test}.cpp)
2224 qt5_use_modules(${_test}Exec Test Core Qml)
2325 set_tests_properties(test${CLASSNAME}${_test}
2426 PROPERTIES ENVIRONMENT LD_LIBRARY_PATH=${CMAKE_BINARY_DIR}/plugins/Unity:${LIBUNITYPROTO_LIBRARY_DIRS})
@@ -26,6 +28,7 @@ macro(run_tests)
2628 target_link_libraries(${_test}Exec
2729 Unity-qml
2830 ${UNITYCORE_LDFLAGS}
31+ ${LIBUNITY_LDFLAGS}
2932 ${LIBUNITYPROTO_LDFLAGS}
3033 )
3134
@@ -35,4 +38,5 @@ endmacro(run_tests)
3538
3639run_tests(
3740 previewbindingstest
41+ filtersbindingstest
3842 )
tests/plugins/Unity/filtersbindingstest.cppnew file+214
@@ -0,0 +1,214 @@
1+/*
2+* Copyright (C) 2011, 2013 Canonical, Ltd.
3+*
4+* This program is free software; you can redistribute it and/or modify
5+* it under the terms of the GNU General Public License as published by
6+* the Free Software Foundation; version 3.
7+*
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 General Public License for more details.
12+*
13+* You should have received a copy of the GNU General Public License
14+* along with this program. If not, see <http://www.gnu.org/licenses/>.
15+*/
16+
17+#include "filtersbindingstest.h"
18+
19+// libunity-core
20+#include <UnityCore/Filter.h>
21+#include <UnityCore/MultiRangeFilter.h>
22+
23+// Qt
24+#include <QTest>
25+#include <QSignalSpy>
26+
27+// libunity
28+#include <unity.h>
29+
30+#include <dee.h>
31+
32+// local
33+#include "filter.h"
34+#include "multirangefilter.h"
35+#include "combinedfilteroption.h"
36+
37+void FiltersBindingsTest::initTestCase()
38+{
39+}
40+
41+GVariant *createOptions(int numOfOptions)
42+{
43+ auto vtype = g_variant_type_new("a(sssb)");
44+ auto vb = g_variant_builder_new(vtype);
45+ for (int i = 0; i<numOfOptions; i++) {
46+ const std::string optId = "opt" + std::to_string(i);
47+ const std::string optName = "Option " + std::to_string(i);
48+ g_variant_builder_add(vb, "(sssb)", optId.c_str(), optName.c_str(), "", false);
49+ }
50+ return g_variant_builder_end(vb);
51+}
52+
53+DeeModel* createFilterModel()
54+{
55+ const char* filter_schema[8] = {"s", "s", "s", "s", "a{sv}", "b", "b", "b"};
56+ auto model = dee_sequence_model_new();
57+ dee_model_set_schema_full (DEE_MODEL(model), filter_schema, 8);
58+ return model;
59+}
60+
61+void createMultiRangeFilter(DeeModel *model, const std::string &id, const std::string &name, int optionCount)
62+{
63+ GVariant* row[8];
64+
65+ row[0] = g_variant_new_string(id.c_str());
66+ row[1] = g_variant_new_string(name.c_str());
67+ row[2] = g_variant_new_string(""); // icon hint
68+ row[3] = g_variant_new_string("filter-multirange"); // renderer
69+
70+ GVariant *children[2];
71+ GVariant *key1 = g_variant_new_string("show-all-button");
72+ GVariant *key2 = g_variant_new_string("options");
73+ children[0] = g_variant_new_dict_entry(key1, g_variant_new_variant(g_variant_new_boolean(false)));
74+ children[1] = g_variant_new_dict_entry(key2, g_variant_new_variant(createOptions(optionCount)));
75+ auto fhints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, 2);
76+
77+ row[4] = fhints;
78+ row[5] = g_variant_new_boolean(true); // visible
79+ row[6] = g_variant_new_boolean(false); // collapsed
80+ row[7] = g_variant_new_boolean(false); // filtering
81+
82+ dee_model_append_row(model, row);
83+}
84+
85+void FiltersBindingsTest::testMultiRangeFilter()
86+{
87+ auto model = createFilterModel();
88+
89+ createMultiRangeFilter(model, "f1", "Filter1", 1);
90+ createMultiRangeFilter(model, "f2", "Filter2", 2);
91+ createMultiRangeFilter(model, "f3", "Filter3", 3);
92+
93+ // create filter out of 1st row
94+ auto iter = dee_model_get_first_iter(DEE_MODEL(model));
95+ {
96+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
97+ QVERIFY(core_filter != nullptr);
98+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
99+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
100+ QVERIFY(multi_filter != nullptr);
101+ auto options = multi_filter->options();
102+ QCOMPARE(options->rowCount(), 1);
103+
104+ QVariant optvar = options->data(options->index(0));
105+ CombinedFilterOption* opt1 = optvar.value<CombinedFilterOption*>();
106+ QCOMPARE(opt1->id(), QString("opt0"));
107+
108+ delete multi_filter;
109+ }
110+
111+ // create filter out of 2nd row
112+ iter = dee_model_next(DEE_MODEL(model), iter);
113+ {
114+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
115+ QVERIFY(core_filter != nullptr);
116+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
117+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
118+ QVERIFY(multi_filter != nullptr);
119+ auto options = multi_filter->options();
120+ QCOMPARE(options->rowCount(), 2);
121+
122+ QVariant optvar = options->data(options->index(0));
123+ CombinedFilterOption* opt1 = optvar.value<CombinedFilterOption*>();
124+ QCOMPARE(opt1->id(), QString("opt0-opt1"));
125+
126+ optvar = options->data(options->index(1));
127+ CombinedFilterOption* opt2 = optvar.value<CombinedFilterOption*>();
128+ QCOMPARE(opt2->id(), QString("opt1"));
129+
130+ delete multi_filter;
131+ }
132+
133+ // create filter out of 3nd row
134+ iter = dee_model_next(DEE_MODEL(model), iter);
135+ {
136+ auto core_filter = unity::dash::Filter::FilterFromIter(model, iter);
137+ QVERIFY(core_filter != nullptr);
138+ auto bind_filter = Filter::newFromUnityFilter(core_filter);
139+ auto multi_filter = dynamic_cast<MultiRangeFilter*>(bind_filter);
140+ QVERIFY(multi_filter != nullptr);
141+ auto options = multi_filter->options();
142+ QCOMPARE(options->rowCount(), 3);
143+
144+ QVariant optvar = options->data(options->index(0));
145+ CombinedFilterOption* opt0 = optvar.value<CombinedFilterOption*>();
146+ QCOMPARE(opt0->id(), QString("opt0-opt1"));
147+
148+ optvar = options->data(options->index(1));
149+ CombinedFilterOption* opt1 = optvar.value<CombinedFilterOption*>();
150+ QCOMPARE(opt1->id(), QString("opt1-opt2"));
151+
152+ optvar = options->data(options->index(2));
153+ CombinedFilterOption* opt2 = optvar.value<CombinedFilterOption*>();
154+ QCOMPARE(opt2->id(), QString("opt2"));
155+
156+ // verify combined filter options are bound to correct underlying unity options
157+ auto core_multi_filter = std::dynamic_pointer_cast<unity::dash::MultiRangeFilter>(core_filter);
158+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[0]->id()), QString("opt0"));
159+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[1]->id()), QString("opt1"));
160+ QCOMPARE(QString::fromStdString(core_multi_filter->options()[2]->id()), QString("opt2"));
161+
162+ QSignalSpy opt0spy(opt0, SIGNAL(activeChanged(bool)));
163+ QSignalSpy opt1spy(opt1, SIGNAL(activeChanged(bool)));
164+ QSignalSpy opt2spy(opt2, SIGNAL(activeChanged(bool)));
165+
166+ // test active property changes
167+ QCOMPARE(opt0->active(), false);
168+
169+ opt0->setActive(true); // activate 1st combined option
170+ QCOMPARE(opt0spy.count(), 1);
171+ QCOMPARE(opt1spy.count(), 0);
172+ QCOMPARE(opt2spy.count(), 0);
173+ QCOMPARE(opt0->active(), true);
174+ QCOMPARE(opt1->active(), false);
175+ QCOMPARE(core_multi_filter->options()[0]->active(), true);
176+ QCOMPARE(core_multi_filter->options()[1]->active(), true);
177+ QCOMPARE(core_multi_filter->options()[2]->active(), false);
178+
179+ opt1->setActive(true); // activate another combined option
180+ QCOMPARE(opt0spy.count(), 2);
181+ QCOMPARE(opt1spy.count(), 1);
182+ QCOMPARE(opt2spy.count(), 0);
183+ QCOMPARE(opt0->active(), false); //1st combined option gets deactivated
184+ QCOMPARE(opt1->active(), true);
185+ QCOMPARE(core_multi_filter->options()[0]->active(), false);
186+ QCOMPARE(core_multi_filter->options()[1]->active(), true); // shared option, stays active
187+ QCOMPARE(core_multi_filter->options()[2]->active(), true);
188+
189+ opt1->setActive(false); // and de-activate it
190+ QCOMPARE(opt0spy.count(), 2);
191+ QCOMPARE(opt1spy.count(), 2);
192+ QCOMPARE(opt2spy.count(), 0);
193+ QCOMPARE(opt0->active(), false);
194+ QCOMPARE(opt1->active(), false);
195+ QCOMPARE(core_multi_filter->options()[0]->active(), false);
196+ QCOMPARE(core_multi_filter->options()[1]->active(), false);
197+ QCOMPARE(core_multi_filter->options()[2]->active(), false);
198+
199+ opt2->setActive(true); // activate another combined option
200+ QCOMPARE(opt0spy.count(), 2);
201+ QCOMPARE(opt1spy.count(), 2);
202+ QCOMPARE(opt2spy.count(), 1);
203+ QCOMPARE(opt0->active(), false);
204+ QCOMPARE(opt1->active(), false);
205+ QCOMPARE(opt2->active(), true);
206+ QCOMPARE(core_multi_filter->options()[0]->active(), false);
207+ QCOMPARE(core_multi_filter->options()[1]->active(), false);
208+ QCOMPARE(core_multi_filter->options()[2]->active(), true);
209+
210+ delete multi_filter;
211+ }
212+}
213+
214+QTEST_MAIN(FiltersBindingsTest)
tests/plugins/Unity/filtersbindingstest.hnew file+31
@@ -0,0 +1,31 @@
1+/*
2+ * Copyright (C) 2011, 2013 Canonical, Ltd.
3+ *
4+ * This program is free software; you can redistribute it and/or modify
5+ * it under the terms of the GNU General Public License as published by
6+ * the Free Software Foundation; version 3.
7+ *
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 General Public License for more details.
12+ *
13+ * You should have received a copy of the GNU General Public License
14+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
15+ */
16+
17+#ifndef FILTERSBINDINGSTEST_H
18+#define FILTERSBINDINGSTEST_H
19+
20+#include <QObject>
21+
22+class FiltersBindingsTest : public QObject
23+{
24+ Q_OBJECT
25+
26+ private Q_SLOTS:
27+ void initTestCase();
28+ void testMultiRangeFilter();
29+};
30+
31+#endif