Merge lp:~kalikiana/dee-qt/deevarianttext into lp:dee-qt/0.2

Proposed by Michał Sawicz
Status: Superseded
Proposed branch: lp:~kalikiana/dee-qt/deevarianttext
Merge into: lp:dee-qt/0.2
Prerequisite: lp:~saviq/dee-qt/rename-libs
Diff against target: 995 lines (+586/-58)
18 files modified
.bzrignore (+15/-2)
CMakeLists.txt (+9/-2)
deelistmodel.cpp (+55/-22)
deelistmodel.h (+7/-2)
deeprivate.h (+23/-0)
deevarianttext.cpp (+72/-0)
deevarianttext.h (+49/-0)
modules/Dee/CMakeLists.txt (+3/-1)
modules/Dee/plugin.cpp (+2/-0)
tests/CMakeLists.txt (+29/-1)
tests/conversiontest.cpp (+1/-0)
tests/deelistmodeltest.cpp (+40/-2)
tests/deevarianttexttest.cpp (+58/-0)
tests/qtquick1plugintest.cpp (+48/-0)
tests/test_qtquick1.qml (+36/-11)
tests/test_qtquick2.qml (+0/-15)
tests/tst_deelistmodel.qml (+79/-0)
tests/tst_deevarianttext.qml (+60/-0)
To merge this branch: bzr merge lp:~kalikiana/dee-qt/deevarianttext
Reviewer Review Type Date Requested Status
Michał Sawicz Needs Fixing
Review via email: mp+137980@code.launchpad.net

This proposal supersedes a proposal from 2012-12-04.

This proposal has been superseded by a proposal from 2013-03-14.

To post a comment you must log in.
Revision history for this message
Michał Sawicz (saviq) wrote :

Your changes to DeeListModel broke it for our use (rolesChanged isn't fired anymore).

review: Needs Fixing
lp:~kalikiana/dee-qt/deevarianttext updated
98. By Cris Dywan

Fix running deevarianttexttest

99. By Cris Dywan

Add insertRemoveTest (needs improvement)

100. By Cris Dywan

Fixup insertRemoveTest

101. By Cris Dywan

Use QSignalSpy instead of manual callbacks to verify signals

102. By Cris Dywan

Expose append/ remove to QML to allow hosting new models

For now only in test models and no API to set the schema.

103. By Cris Dywan

Emit layoutChanged in addition to countChanged

104. By Cris Dywan

Use SignalSpy in QML - preliminary text delegate testing

105. By Cris Dywan

Use QT_INSTALL_QML (QtQuick2) instead of QT_INSTALL_IMPORTS (QtQuick1)

See http://doc-snapshot.qt-project.org/5.0/qtcore/qlibraryinfo.html#LibraryLocation-enum

106. By Cris Dywan

Drop delegate test, the approach is wrong

107. By Cris Dywan

Drop remaining pieces of delegate approach as well

Unmerged revisions

107. By Cris Dywan

Drop remaining pieces of delegate approach as well

106. By Cris Dywan

Drop delegate test, the approach is wrong

105. By Cris Dywan

Use QT_INSTALL_QML (QtQuick2) instead of QT_INSTALL_IMPORTS (QtQuick1)

See http://doc-snapshot.qt-project.org/5.0/qtcore/qlibraryinfo.html#LibraryLocation-enum

104. By Cris Dywan

Use SignalSpy in QML - preliminary text delegate testing

103. By Cris Dywan

Emit layoutChanged in addition to countChanged

102. By Cris Dywan

Expose append/ remove to QML to allow hosting new models

For now only in test models and no API to set the schema.

101. By Cris Dywan

Use QSignalSpy instead of manual callbacks to verify signals

100. By Cris Dywan

Fixup insertRemoveTest

99. By Cris Dywan

Add insertRemoveTest (needs improvement)

98. By Cris Dywan

Fix running deevarianttexttest

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file '.bzrignore'
--- .bzrignore 2013-03-08 16:59:19 +0000
+++ .bzrignore 2013-03-08 16:59:20 +0000
@@ -2,7 +2,20 @@
2CMakeFiles2CMakeFiles
3Makefile3Makefile
4cmake_install.cmake4cmake_install.cmake
5install_manifest.txt
5libdee-qt.so*6libdee-qt.so*
6libdee-qt5.so*7libdee-qt5.so*
7moc_*.cxx8libQtDee.so*
8install_manifest.txt9moc_*.cpp
10*.moc
11*_automoc.cpp
12*.pc
13CTestTestfile.cmake
14*Test.log
15Testing
16conversiontest
17deelistmodeltest
18deevarianttexttest
19plugintest
20test-helper
21*-xunit.xml
922
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-03-08 16:59:19 +0000
+++ CMakeLists.txt 2013-03-08 16:59:20 +0000
@@ -18,7 +18,7 @@
18 set(OUR_QT_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS})18 set(OUR_QT_INCLUDES ${Qt5Core_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS})
19 set(OUR_QT_CORE_LIB ${Qt5Core_LIBRARIES})19 set(OUR_QT_CORE_LIB ${Qt5Core_LIBRARIES})
20 set(OUR_QT_DBUS_LIB ${Qt5DBus_LIBRARIES})20 set(OUR_QT_DBUS_LIB ${Qt5DBus_LIBRARIES})
21 set(QT_PKGCONFIG_DEPENDENCIES "QtCore")21 set(QT_PKGCONFIG_DEPENDENCIES "QtCore QtQuick")
22else ()22else ()
23 message("Building Qt4 version")23 message("Building Qt4 version")
2424
@@ -43,6 +43,12 @@
43# Sources43# Sources
44set(DEE_QT_SRCS44set(DEE_QT_SRCS
45 deelistmodel.cpp45 deelistmodel.cpp
46 deevarianttext.cpp
47 )
48
49## QtDeeQml
50set(QtDeeQml_SRCS
51 plugin.cpp
46 )52 )
4753
48# Build54# Build
@@ -69,6 +75,7 @@
69# Unit-Test75# Unit-Test
70enable_testing()76enable_testing()
7177
78add_custom_target(check COMMAND "env" "CTEST_OUTPUT_ON_FAILURE=1" "${CMAKE_CTEST_COMMAND}")
72add_subdirectory(modules)79add_subdirectory(modules)
73add_subdirectory(tests)80add_subdirectory(tests)
7481
@@ -79,7 +86,7 @@
79 LIBRARY DESTINATION lib${LIB_SUFFIX}86 LIBRARY DESTINATION lib${LIB_SUFFIX}
80 )87 )
8188
82install(FILES deelistmodel.h89install(FILES deelistmodel.h deevarianttext.h
83 DESTINATION ${INCLUDE_INSTALL_DIR}90 DESTINATION ${INCLUDE_INSTALL_DIR}
84 )91 )
8592
8693
=== modified file 'deelistmodel.cpp'
--- deelistmodel.cpp 2012-11-21 10:30:50 +0000
+++ deelistmodel.cpp 2013-03-08 16:59:20 +0000
@@ -17,17 +17,20 @@
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */18 */
1919
20#include <QCoreApplication>
20#include <QtCore/QHash>21#include <QtCore/QHash>
21#include <QtCore/QByteArray>22#include <QtCore/QByteArray>
23#include <QDebug>
2224
23#include <dee.h>25#include <dee.h>
24#include <glib-object.h>
2526
26#include "deelistmodel.h"27#include "deelistmodel.h"
28#include "deeprivate.h"
2729
28static QVariant30QVariant
29QVariantFromGVariant(GVariant *value)31QVariantFromGVariant(GVariant *value)
30{32{
33 Q_ASSERT(value);
31 switch (g_variant_classify(value)) {34 switch (g_variant_classify(value)) {
32 case G_VARIANT_CLASS_BOOLEAN:35 case G_VARIANT_CLASS_BOOLEAN:
33 return QVariant((bool) g_variant_get_boolean(value));36 return QVariant((bool) g_variant_get_boolean(value));
@@ -62,13 +65,14 @@
62 }65 }
63 return array;66 return array;
64 }67 }
68 case G_VARIANT_CLASS_VARIANT:
69 return QVariant(QVariantFromGVariant(g_variant_get_variant(value)));
65 default:70 default:
66 /* Fallback on an empty QVariant.71 /* Fallback on an empty QVariant.
67 FIXME: Missing conversion of following GVariant types:72 FIXME: Missing conversion of following GVariant types:
68 - G_VARIANT_CLASS_HANDLE73 - G_VARIANT_CLASS_HANDLE
69 - G_VARIANT_CLASS_OBJECT_PATH74 - G_VARIANT_CLASS_OBJECT_PATH
70 - G_VARIANT_CLASS_SIGNATURE75 - G_VARIANT_CLASS_SIGNATURE
71 - G_VARIANT_CLASS_VARIANT
72 - G_VARIANT_CLASS_MAYBE76 - G_VARIANT_CLASS_MAYBE
73 - G_VARIANT_CLASS_DICT_ENTRY77 - G_VARIANT_CLASS_DICT_ENTRY
74 */78 */
@@ -84,7 +88,6 @@
84 void connectToDeeModel();88 void connectToDeeModel();
85 void connectToDeeModel(DeeModel *model);89 void connectToDeeModel(DeeModel *model);
86 void disconnectFromDeeModel();90 void disconnectFromDeeModel();
87 void createRoles();
88 bool synchronized() const;91 bool synchronized() const;
8992
90 /* GObject signal handlers for m_deeModel */93 /* GObject signal handlers for m_deeModel */
@@ -132,16 +135,27 @@
132 if (!m_name.isEmpty())135 if (!m_name.isEmpty())
133 {136 {
134 m_deeModel = dee_shared_model_new(m_name.toUtf8().data());137 m_deeModel = dee_shared_model_new(m_name.toUtf8().data());
138 // FIXME expose property to set schema in QML
139 bool ownSchema = m_name.contains(".test");
140 if (ownSchema)
141 dee_model_set_schema(m_deeModel, "b", NULL);
135 g_signal_connect(m_deeModel, "notify::synchronized", G_CALLBACK(onSynchronizedChanged), m_parent);142 g_signal_connect(m_deeModel, "notify::synchronized", G_CALLBACK(onSynchronizedChanged), m_parent);
136 g_signal_connect(m_deeModel, "row-added", G_CALLBACK(onRowAdded), m_parent);143 g_signal_connect(m_deeModel, "row-added", G_CALLBACK(onRowAdded), m_parent);
137 g_signal_connect(m_deeModel, "row-removed", G_CALLBACK(onRowRemoved), m_parent);144 g_signal_connect(m_deeModel, "row-removed", G_CALLBACK(onRowRemoved), m_parent);
138 g_signal_connect(m_deeModel, "row-changed", G_CALLBACK(onRowChanged), m_parent);145 g_signal_connect(m_deeModel, "row-changed", G_CALLBACK(onRowChanged), m_parent);
146 if (ownSchema)
147 {
148 // Doc says we need to be synchronized before doing anything
149 while(!dee_shared_model_is_synchronized(DEE_SHARED_MODEL(m_deeModel)))
150 qApp->processEvents();
151 }
139 }152 }
140}153}
141154
142void155void
143DeeListModelPrivate::connectToDeeModel(DeeModel *model)156DeeListModelPrivate::connectToDeeModel(DeeModel *model)
144{157{
158 m_parent->beginResetModel();
145 disconnectFromDeeModel();159 disconnectFromDeeModel();
146160
147 m_deeModel = (DeeModel*)g_object_ref (model);161 m_deeModel = (DeeModel*)g_object_ref (model);
@@ -150,15 +164,15 @@
150 g_signal_connect(m_deeModel, "row-changed", G_CALLBACK(onRowChanged), m_parent);164 g_signal_connect(m_deeModel, "row-changed", G_CALLBACK(onRowChanged), m_parent);
151 if (synchronized())165 if (synchronized())
152 {166 {
153 createRoles();
154 m_count = dee_model_get_n_rows(m_deeModel);167 m_count = dee_model_get_n_rows(m_deeModel);
155 m_parent->reset();
156 Q_EMIT m_parent->countChanged();168 Q_EMIT m_parent->countChanged();
169 Q_EMIT m_parent->layoutChanged();
157 }170 }
158 else171 else
159 {172 {
160 g_signal_connect(m_deeModel, "notify::synchronized", G_CALLBACK(onSynchronizedChanged), m_parent);173 g_signal_connect(m_deeModel, "notify::synchronized", G_CALLBACK(onSynchronizedChanged), m_parent);
161 }174 }
175 m_parent->endResetModel();
162}176}
163177
164bool178bool
@@ -175,25 +189,43 @@
175 }189 }
176}190}
177191
178void192QHash<int, QByteArray>
179DeeListModelPrivate::createRoles()193DeeListModel::roleNames()
180{194{
181 if (m_deeModel == NULL) {
182 return;
183 }
184
185 QHash<int, QByteArray> roles;195 QHash<int, QByteArray> roles;
196
197 qDebug() << "m_deeModel" << d->m_deeModel;
198 if (d->m_deeModel == NULL) {
199 return roles;
200 }
201
186 QString column;202 QString column;
187 guint n_columns = dee_model_get_n_columns(m_deeModel);203 guint n_columns = dee_model_get_n_columns(d->m_deeModel);
188204
189 for (unsigned int index=0; index<n_columns; index++)205 for (unsigned int index=0; index<n_columns; index++)
190 {206 {
191 column = QString("column_%1").arg(index);207 column = QString("column_%1").arg(index);
192 roles[index] = column.toAscii();208 roles[index] = column.toUtf8();
193 }209 }
194210 return roles;
195 m_parent->setRoleNames(roles);211}
196 Q_EMIT m_parent->roleNamesChanged(roles);212
213void DeeListModel::append(const QString & data)
214{
215 GVariant* gvariant = g_variant_parse(NULL, "false", NULL, NULL, NULL);
216 if (!gvariant)
217 {
218 qDebug() << "Failed to append " << data << " (invalid) to model";
219 return;
220 }
221 DeeModelIter* iter = dee_model_insert (d->m_deeModel, 0, &gvariant);
222 g_variant_unref(gvariant);
223}
224
225void DeeListModel::remove(int index)
226{
227 DeeModelIter* iter = dee_model_get_iter_at_row (d->m_deeModel, index);
228 dee_model_remove(d->m_deeModel, iter);
197}229}
198230
199void231void
@@ -201,11 +233,12 @@
201 GParamSpec *pspec,233 GParamSpec *pspec,
202 DeeListModel *model)234 DeeListModel *model)
203{235{
204 model->d->createRoles();236 model->beginResetModel();
205 model->d->m_count = dee_model_get_n_rows(model->d->m_deeModel);237 model->d->m_count = dee_model_get_n_rows(model->d->m_deeModel);
206 model->synchronizedChanged(model->synchronized());238 model->synchronizedChanged(model->synchronized());
207 model->reset();
208 Q_EMIT model->countChanged();239 Q_EMIT model->countChanged();
240 Q_EMIT model->layoutChanged();
241 model->endResetModel();
209}242}
210243
211void244void
@@ -228,6 +261,7 @@
228 model->beginInsertRows(QModelIndex(), position, position);261 model->beginInsertRows(QModelIndex(), position, position);
229 model->endInsertRows();262 model->endInsertRows();
230 Q_EMIT model->countChanged();263 Q_EMIT model->countChanged();
264 Q_EMIT model->layoutChanged();
231}265}
232266
233void267void
@@ -255,6 +289,7 @@
255 model->beginRemoveRows(QModelIndex(), position, position);289 model->beginRemoveRows(QModelIndex(), position, position);
256 model->endRemoveRows();290 model->endRemoveRows();
257 Q_EMIT model->countChanged();291 Q_EMIT model->countChanged();
292 Q_EMIT model->layoutChanged();
258}293}
259294
260void295void
@@ -271,8 +306,6 @@
271 Q_EMIT model->dataChanged(index, index);306 Q_EMIT model->dataChanged(index, index);
272}307}
273308
274
275
276DeeListModel::DeeListModel(QObject *parent) :309DeeListModel::DeeListModel(QObject *parent) :
277 QAbstractListModel(parent), d(new DeeListModelPrivate(this))310 QAbstractListModel(parent), d(new DeeListModelPrivate(this))
278{311{
279312
=== modified file 'deelistmodel.h'
--- deelistmodel.h 2012-01-31 17:49:00 +0000
+++ deelistmodel.h 2013-03-08 16:59:20 +0000
@@ -20,11 +20,12 @@
20#ifndef DEELISTMODEL_H20#ifndef DEELISTMODEL_H
21#define DEELISTMODEL_H21#define DEELISTMODEL_H
2222
23#include <QtCore/QObject>
23#include <QtCore/QAbstractListModel>24#include <QtCore/QAbstractListModel>
2425
25class DeeListModelPrivate;26class DeeListModelPrivate;
26typedef struct _DeeModel DeeModel;27typedef struct _DeeModel DeeModel;
27class __attribute__ ((visibility ("default"))) DeeListModel : public QAbstractListModel28class Q_DECL_EXPORT DeeListModel : public QAbstractListModel
28{29{
29 friend class DeeListModelPrivate;30 friend class DeeListModelPrivate;
3031
@@ -45,6 +46,10 @@
45 QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;46 QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const;
46 int rowCount(const QModelIndex & parent = QModelIndex()) const;47 int rowCount(const QModelIndex & parent = QModelIndex()) const;
4748
49 /* Modification of the model */
50 Q_INVOKABLE void append(const QString & data);
51 Q_INVOKABLE void remove(int index);
52
48 /* getters */53 /* getters */
49 QString name() const;54 QString name() const;
50 bool synchronized() const;55 bool synchronized() const;
@@ -52,11 +57,11 @@
52 /* setters */57 /* setters */
53 void setName(const QString& name);58 void setName(const QString& name);
54 void setModel(DeeModel* model);59 void setModel(DeeModel* model);
60 QHash<int, QByteArray> roleNames();
5561
56Q_SIGNALS:62Q_SIGNALS:
57 void nameChanged(const QString&);63 void nameChanged(const QString&);
58 void synchronizedChanged(bool);64 void synchronizedChanged(bool);
59 void roleNamesChanged(const QHash<int,QByteArray> &);
60 void countChanged();65 void countChanged();
6166
62private:67private:
6368
=== added file 'deeprivate.h'
--- deeprivate.h 1970-01-01 00:00:00 +0000
+++ deeprivate.h 2013-03-08 16:59:20 +0000
@@ -0,0 +1,23 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * Authors:
5 * Christian Dywan <christian.dywan@canonical.com>
6 *
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.
10 *
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.
15 *
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/>.
18 */
19
20#include <glib-object.h>
21
22QVariant QVariantFromGVariant(GVariant *value);
23
024
=== added file 'deevarianttext.cpp'
--- deevarianttext.cpp 1970-01-01 00:00:00 +0000
+++ deevarianttext.cpp 2013-03-08 16:59:20 +0000
@@ -0,0 +1,72 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * Authors:
5 * Christian Dywan <christian.dywan@canonical.com>
6 *
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.
10 *
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.
15 *
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/>.
18 */
19
20#include <QDebug>
21
22#include "deevarianttext.h"
23#include "deeprivate.h"
24
25DeeVariantText::DeeVariantText(QObject *parent) :
26 m_text(""),
27 m_value(QVariant()),
28 m_type("")
29{
30}
31
32void
33DeeVariantText::setText(const QString& text)
34{
35 if (m_text == text)
36 return;
37
38 m_text = text;
39 GVariant *gvariant = g_variant_parse(NULL, text.toUtf8(), NULL, NULL, NULL);
40 if (!gvariant)
41 {
42 qDebug() << "Failed to parse " << text << " to QVariant";
43 m_value = QVariant();
44 m_type = "";
45 }
46 else
47 {
48 m_value = QVariantFromGVariant(gvariant);
49 m_type = g_variant_get_type_string(gvariant);
50 }
51 Q_EMIT textChanged(text);
52 Q_EMIT valueChanged(m_value);
53}
54
55QString
56DeeVariantText::getText()
57{
58 return m_text;
59}
60
61QVariant
62DeeVariantText::getValue()
63{
64 return m_value;
65}
66
67QVariant
68DeeVariantText::getType()
69{
70 return m_type;
71}
72
073
=== added file 'deevarianttext.h'
--- deevarianttext.h 1970-01-01 00:00:00 +0000
+++ deevarianttext.h 2013-03-08 16:59:20 +0000
@@ -0,0 +1,49 @@
1/*
2 * Copyright (C) 2012 Canonical, Ltd.
3 *
4 * Authors:
5 * Christian Dywan <christian.dywan@canonical.com>
6 *
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.
10 *
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.
15 *
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/>.
18 */
19
20#ifndef DEEVARIANTTEXT_H
21#define DEEVARIANTTEXT_H
22
23#include <QtCore/QObject>
24#include <QtCore/QVariant>
25
26class Q_DECL_EXPORT DeeVariantText : public QObject {
27 Q_OBJECT
28 Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
29 Q_PROPERTY(QVariant value READ getValue NOTIFY valueChanged)
30public:
31 DeeVariantText(QObject* parent = 0);
32 ~DeeVariantText() { }
33
34 QString getText();
35 QVariant getValue();
36 QVariant getType();
37 void setText(const QString& text);
38Q_SIGNALS:
39 void textChanged(const QString& text);
40 void valueChanged(const QVariant& value);
41private:
42 Q_DISABLE_COPY(DeeVariantText)
43 QString m_text;
44 QVariant m_value;
45 QString m_type;
46};
47
48#endif // DEEVARIANTTEXT_H
49
050
=== modified file 'modules/Dee/CMakeLists.txt'
--- modules/Dee/CMakeLists.txt 2013-03-08 16:59:19 +0000
+++ modules/Dee/CMakeLists.txt 2013-03-08 16:59:20 +0000
@@ -4,7 +4,8 @@
4 set(OUR_QT_QUICK_LIB ${Qt5Quick_LIBRARIES})4 set(OUR_QT_QUICK_LIB ${Qt5Quick_LIBRARIES})
55
6 get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)6 get_target_property(QMAKE_EXECUTABLE Qt5::qmake LOCATION)
7 exec_program(${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_IMPORTS" OUTPUT_VARIABLE QT_IMPORTS_DIR)7 # See http://doc-snapshot.qt-project.org/5.0/qtcore/qlibraryinfo.html#LibraryLocation-enum
8 exec_program(${QMAKE_EXECUTABLE} ARGS "-query QT_INSTALL_QML" OUTPUT_VARIABLE QT_IMPORTS_DIR)
8 file(TO_CMAKE_PATH "${QT_IMPORTS_DIR}" QT_IMPORTS_DIR)9 file(TO_CMAKE_PATH "${QT_IMPORTS_DIR}" QT_IMPORTS_DIR)
910
10 set(OUR_QT_QUICK_INCLUDE ${Qt5Quick_INCLUDE_DIRS})11 set(OUR_QT_QUICK_INCLUDE ${Qt5Quick_INCLUDE_DIRS})
@@ -21,6 +22,7 @@
21 )22 )
2223
23add_library(DeePlugin SHARED ${DeePlugin_SRCS})24add_library(DeePlugin SHARED ${DeePlugin_SRCS})
25add_dependencies(check DeePlugin)
2426
25target_link_libraries(DeePlugin27target_link_libraries(DeePlugin
26 ${DEE_QT_LIBNAME}28 ${DEE_QT_LIBNAME}
2729
=== modified file 'modules/Dee/plugin.cpp'
--- modules/Dee/plugin.cpp 2013-03-08 16:59:19 +0000
+++ modules/Dee/plugin.cpp 2013-03-08 16:59:20 +0000
@@ -18,6 +18,7 @@
18 */18 */
1919
20#include "deelistmodel.h"20#include "deelistmodel.h"
21#include "deevarianttext.h"
21#include "plugin.h"22#include "plugin.h"
22#if WITHQT523#if WITHQT5
23 #include <qqml.h>24 #include <qqml.h>
@@ -28,6 +29,7 @@
28void DeePlugin::registerTypes(const char *uri)29void DeePlugin::registerTypes(const char *uri)
29{30{
30 qmlRegisterType<DeeListModel>(uri, 3, 0, "DeeListModel");31 qmlRegisterType<DeeListModel>(uri, 3, 0, "DeeListModel");
32 qmlRegisterType<DeeVariantText>(uri, 3, 0, "DeeVariantText");
31}33}
3234
33#if !WITHQT535#if !WITHQT5
3436
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2013-03-08 16:59:19 +0000
+++ tests/CMakeLists.txt 2013-03-08 16:59:20 +0000
@@ -5,25 +5,53 @@
5else ()5else ()
6 set(OUR_QT_TEST_LIB ${QT_QTTEST_LIBRARIES})6 set(OUR_QT_TEST_LIB ${QT_QTTEST_LIBRARIES})
7 set(OUR_QT_TEST_INCLUDES ${QT_QTTEST_INCLUDE_DIR})7 set(OUR_QT_TEST_INCLUDES ${QT_QTTEST_INCLUDE_DIR})
8 set(OUR_QT_QUICK_LIB ${QT_QTDECLARATIVE_LIBRARIES})
9 set(OUR_QT_QUICK_INCLUDE ${QT_QTDECLARATIVE_INCLUDE_DIR})
10endif ()
11
12if (WITHQT5)
13 add_test(NAME plugintest COMMAND "qmltestrunner" "-import" "../modules" "-xunitxml" "-o" "plugintest-xunit.xml" "-input" "${CMAKE_CURRENT_SOURCE_DIR}")
14else ()
15 add_executable(plugintest qtquick1plugintest.cpp)
16 target_link_libraries(plugintest ${OUR_QT_TEST_LIB} ${OUR_QT_QUICK_LIB})
17 set_target_properties(plugintest PROPERTIES COMPILE_FLAGS -fPIC)
18 add_test(NAME plugintest WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMAND "dbus-test-runner" "--task" "${CMAKE_CURRENT_BINARY_DIR}/plugintest" "-p" "-xunitxml" "-p" "-o" "-p" "${CMAKE_CURRENT_BINARY_DIR}/plugintest-xunit.xml")
19 set_property(TEST plugintest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")
20 set_property(TEST plugintest PROPERTY ENVIRONMENT "QT_GRAPHICSSYSTEM=raster")
21 set_property(TEST plugintest PROPERTY ENVIRONMENT "QML_IMPORT_PATH=${CMAKE_CURRENT_BINARY_DIR}/../modules")
22 add_dependencies(check plugintest)
8endif ()23endif ()
924
10include_directories(25include_directories(
11 ${CMAKE_CURRENT_BINARY_DIR}26 ${CMAKE_CURRENT_BINARY_DIR}
12 ${OUR_QT_TEST_INCLUDES}27 ${OUR_QT_TEST_INCLUDES}
28 ${OUR_QT_QUICK_INCLUDE}
13 )29 )
1430
15add_executable(conversiontest conversiontest.cpp)31add_executable(conversiontest conversiontest.cpp)
16target_link_libraries(conversiontest ${OUR_QT_TEST_LIB} ${DEE_QT_LIBNAME})32target_link_libraries(conversiontest ${OUR_QT_WIDGETS_LIB} ${OUR_QT_TEST_LIB} ${OUR_QT_QUICK_LIB} ${DEE_QT_LIBNAME})
17set_target_properties(conversiontest PROPERTIES COMPILE_FLAGS -fPIC)33set_target_properties(conversiontest PROPERTIES COMPILE_FLAGS -fPIC)
18add_test(NAME conversiontest COMMAND "dbus-test-runner" "--task" "${CMAKE_CURRENT_BINARY_DIR}/conversiontest" "-p" "-xunitxml" "-p" "-o" "-p" "conversiontest-xunit.xml")34add_test(NAME conversiontest COMMAND "dbus-test-runner" "--task" "${CMAKE_CURRENT_BINARY_DIR}/conversiontest" "-p" "-xunitxml" "-p" "-o" "-p" "conversiontest-xunit.xml")
19set_property(TEST conversiontest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")35set_property(TEST conversiontest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")
36set_property(TEST conversiontest PROPERTY ENVIRONMENT "QT_QPA_PLATFORM=minimal")
37add_dependencies(check conversiontest)
2038
21add_executable(test-helper test-helper.cpp)39add_executable(test-helper test-helper.cpp)
22target_link_libraries(test-helper ${OUR_QT_CORE_LIB} ${OUR_QT_DBUS_LIB} ${DEE_LDFLAGS})40target_link_libraries(test-helper ${OUR_QT_CORE_LIB} ${OUR_QT_DBUS_LIB} ${DEE_LDFLAGS})
23set_target_properties(test-helper PROPERTIES COMPILE_FLAGS -fPIC)41set_target_properties(test-helper PROPERTIES COMPILE_FLAGS -fPIC)
42add_dependencies(conversiontest ${DEE_QT_LIBNAME} test-helper)
2443
25add_executable(deelistmodeltest deelistmodeltest.cpp)44add_executable(deelistmodeltest deelistmodeltest.cpp)
26target_link_libraries(deelistmodeltest ${OUR_QT_TEST_LIB} ${OUR_QT_DBUS_LIB} ${DEE_QT_LIBNAME})45target_link_libraries(deelistmodeltest ${OUR_QT_TEST_LIB} ${OUR_QT_DBUS_LIB} ${DEE_QT_LIBNAME})
27set_target_properties(deelistmodeltest PROPERTIES COMPILE_FLAGS -fPIC)46set_target_properties(deelistmodeltest PROPERTIES COMPILE_FLAGS -fPIC)
28add_test(NAME deelistmodeltest COMMAND "dbus-test-runner" "--task" "${CMAKE_CURRENT_BINARY_DIR}/test-helper" "--task" "${CMAKE_CURRENT_BINARY_DIR}/deelistmodeltest" "-p" "-xunitxml" "-p" "-o" "-p" "deelistmodeltest-xunit.xml")47add_test(NAME deelistmodeltest COMMAND "dbus-test-runner" "--task" "${CMAKE_CURRENT_BINARY_DIR}/test-helper" "--task" "${CMAKE_CURRENT_BINARY_DIR}/deelistmodeltest" "-p" "-xunitxml" "-p" "-o" "-p" "deelistmodeltest-xunit.xml")
29set_property(TEST deelistmodeltest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")48set_property(TEST deelistmodeltest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")
49add_dependencies(check deelistmodeltest)
50
51add_executable(deevarianttexttest deevarianttexttest.cpp)
52target_link_libraries(deevarianttexttest ${OUR_QT_TEST_LIB} ${OUR_QT_DBUS_LIB} ${DEE_QT_LIBNAME})
53set_target_properties(deevarianttexttest PROPERTIES COMPILE_FLAGS -fPIC)
54add_test(NAME deevarianttexttest COMMAND "dbus-test-runner" "--task" "${CMAKE_CURRENT_BINARY_DIR}/deevarianttexttest" "-p" "-xunitxml" "-p" "-o" "-p" "deevarianttexttest-xunit.xml")
55set_property(TEST deevarianttexttest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")
56add_dependencies(check deevarianttexttest)
57
3058
=== modified file 'tests/conversiontest.cpp'
--- tests/conversiontest.cpp 2012-11-21 12:09:44 +0000
+++ tests/conversiontest.cpp 2013-03-08 16:59:20 +0000
@@ -18,6 +18,7 @@
18#include <QObject>18#include <QObject>
1919
20#include "deelistmodel.h"20#include "deelistmodel.h"
21#include "deevarianttext.h"
2122
22#include <dee.h>23#include <dee.h>
2324
2425
=== modified file 'tests/deelistmodeltest.cpp'
--- tests/deelistmodeltest.cpp 2012-11-21 12:22:10 +0000
+++ tests/deelistmodeltest.cpp 2013-03-08 16:59:20 +0000
@@ -80,9 +80,11 @@
8080
81 DeeListModel model_qt;81 DeeListModel model_qt;
82 QCOMPARE(model_qt.count(), 0);82 QCOMPARE(model_qt.count(), 0);
83 QSignalSpy modelReset(&model_qt, SIGNAL(modelReset()));
8384
84 model_qt.setModel(model);85 model_qt.setModel(model);
85 QCOMPARE(model_qt.synchronized(), (bool)dee_shared_model_is_synchronized(DEE_SHARED_MODEL(model)));86 QCOMPARE(model_qt.synchronized(), (bool)dee_shared_model_is_synchronized(DEE_SHARED_MODEL(model)));
87 QCOMPARE(modelReset.count(), 1);
86 }88 }
8789
88 void setExistingModelTest()90 void setExistingModelTest()
@@ -90,10 +92,12 @@
90 DeeModel* model = dee_shared_model_new("com.deeqt.test.model");92 DeeModel* model = dee_shared_model_new("com.deeqt.test.model");
9193
92 DeeListModel model_qt;94 DeeListModel model_qt;
95 QSignalSpy modelReset(&model_qt, SIGNAL(modelReset()));
93 QCOMPARE(model_qt.count(), 0);96 QCOMPARE(model_qt.count(), 0);
9497
95 model_qt.setModel(model);98 model_qt.setModel(model);
96 QCOMPARE(model_qt.synchronized(), false);99 QCOMPARE(model_qt.synchronized(), false);
100 QCOMPARE(modelReset.count(), 1);
97101
98 while(!model_qt.synchronized())102 while(!model_qt.synchronized())
99 qApp->processEvents();103 qApp->processEvents();
@@ -102,12 +106,46 @@
102 QCOMPARE(model_qt.roleNames().count(), 1);106 QCOMPARE(model_qt.roleNames().count(), 1);
103 QCOMPARE(model_qt.roleNames()[0], QByteArray("column_0"));107 QCOMPARE(model_qt.roleNames()[0], QByteArray("column_0"));
104 }108 }
105 109
110 void insertRemoveTest()
111 {
112 /* TODO: "ReferenceError: column_0 is not defined" */
113 DeeModel* model = dee_shared_model_new("com.deeqt.test.model");
114
115 DeeListModel model_qt;
116 QSignalSpy modelReset(&model_qt, SIGNAL(modelReset()));
117 QSignalSpy layoutChanged(&model_qt, SIGNAL(layoutChanged()));
118 QSignalSpy rowsInserted(&model_qt, SIGNAL(rowsInserted(const QModelIndex&, int, int)));
119 QSignalSpy rowsRemoved(&model_qt, SIGNAL(rowsRemoved(const QModelIndex&, int, int)));
120 QCOMPARE(model_qt.count(), 0);
121
122 model_qt.setModel(model);
123 QCOMPARE(model_qt.synchronized(), false);
124 QCOMPARE(modelReset.count(), 1);
125
126 while(!model_qt.synchronized())
127 qApp->processEvents();
128 QCOMPARE(model_qt.synchronized(), true);
129 QCOMPARE(layoutChanged.count(), 1);
130
131 GVariant* b = g_variant_parse(NULL, "false", NULL, NULL, NULL);
132 Q_ASSERT(b);
133 DeeModelIter* iter = dee_model_insert (model, 0, &b);
134 g_variant_unref(b);
135 QCOMPARE(rowsInserted.count(), 1);
136 QCOMPARE(model_qt.rowCount(), 1);
137 QCOMPARE(model_qt.roleNames()[0], QByteArray("column_0"));
138
139 dee_model_remove(model, iter);
140 QCOMPARE(rowsRemoved.count(), 1);
141 QCOMPARE(model_qt.rowCount(), 0);
142 QCOMPARE(model_qt.roleNames()[0], QByteArray("column_0"));
143 }
144
106 void cleanupTestCase()145 void cleanupTestCase()
107 {146 {
108 tell_service_to_exit();147 tell_service_to_exit();
109 }148 }
110
111};149};
112150
113QTEST_MAIN(DeeListModelTest)151QTEST_MAIN(DeeListModelTest)
114152
=== added file 'tests/deevarianttexttest.cpp'
--- tests/deevarianttexttest.cpp 1970-01-01 00:00:00 +0000
+++ tests/deevarianttexttest.cpp 2013-03-08 16:59:20 +0000
@@ -0,0 +1,58 @@
1/*
2 * Copyright (C) 2012 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 <QtTest>
18#include <QObject>
19
20#include "deevarianttext.h"
21
22#include <dee.h>
23
24class DeeVariantTextTest : public QObject
25{
26 Q_OBJECT
27
28private Q_SLOTS:
29 void initTestCase()
30 {
31 g_type_init();
32 }
33
34 void GVariantParseTest()
35 {
36 QHash<QString, QVariant> hash;
37 hash["[[1, 2, 3], [4, 5, 6]]"] = "aai";
38 hash["[[1, 2, 3], [4, 5, 6.0]]"] = "aad";
39 hash["[\"hello\", nothing]"] = "ams";
40 hash["{1: \"one\", 2: \"two\", 3: \"three\"}"] = "a{is}";
41 hash["[just 3, nothing]"] = "ami";
42 // hash["{\"title\": <\"frobit\">, \"enabled\": <true>, width: <800>}"] = "";
43 hash["@ms \"\""] = "ms";
44 QHashIterator<QString, QVariant> i(hash);
45 while (i.hasNext()) {
46 i.next();
47 QString input(i.key());
48 DeeVariantText dvariant;
49 dvariant.setText(input);
50 QCOMPARE(dvariant.getText(), input);
51 QCOMPARE(dvariant.getType(), i.value());
52 }
53 }
54};
55
56QTEST_MAIN(DeeVariantTextTest)
57
58#include "deevarianttexttest.moc"
059
=== added file 'tests/qtquick1plugintest.cpp'
--- tests/qtquick1plugintest.cpp 1970-01-01 00:00:00 +0000
+++ tests/qtquick1plugintest.cpp 2013-03-08 16:59:20 +0000
@@ -0,0 +1,48 @@
1/*
2 * Copyright (C) 2012 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 <QtTest>
18#include <QObject>
19#include <QtDeclarative>
20
21class PluginTest : public QObject
22{
23 Q_OBJECT
24
25private Q_SLOTS:
26 void initTestCase()
27 {
28 }
29
30 void GVariantQMLTest()
31 {
32 // Dummy argc/v to avoid GCC confusing int with Display* on some systems
33 int argc = 0;
34 char *argv[0];
35 QApplication app(argc, argv);
36 QDeclarativeView view;
37 view.setSource(QUrl::fromLocalFile("test_qtquick1.qml"));
38 Q_ASSERT(view.errors().empty());
39 QObject *text2 = view.rootObject()->findChild<QObject*>("text2");
40 Q_ASSERT(text2);
41 QVariant text2text(text2->metaObject()->property(text2->metaObject()->indexOfProperty("text")).read(text2));
42 QCOMPARE(text2text.toString(), QString("4,1.1,0.1,0,0.3,3.6"));
43 }
44};
45
46QTEST_MAIN(PluginTest)
47
48#include "qtquick1plugintest.moc"
049
=== modified file 'tests/test_qtquick1.qml'
--- tests/test_qtquick1.qml 2013-03-08 16:59:19 +0000
+++ tests/test_qtquick1.qml 2013-03-08 16:59:20 +0000
@@ -1,15 +1,40 @@
1import QtQuick 1.01import QtQuick 1.0
2import Dee 3.02import Dee 3.0
33
4ListView {4Item {
5 width: 2005
6 height: 2006 ListView {
7 delegate: Text {7 width: 200
8 x: 668 height: 200
9 y: 939 delegate: Text {
10 text: column_410 x: 66
11 }11 y: 93
12 model: DeeListModel {12 text: column_4
13 name: "com.canonical.Unity.Lens.applications.T1313498309.Results"13 }
14 }14 model: DeeListModel {
15 name: "com.canonical.Unity.Lens.applications.T1313498309.Results"
16 }
17 }
18
19 DeeVariantText {
20 id: arrayOfInt
21 text: "[[1, 2, 3], [4, 5, 6]]"
22 Component.onCompleted: {
23 arrayOfInt.text = "[[4, 1.1, .1], [0, 0.3, 3.6]]"
24 }
25 onTextChanged: {
26 text2.text = arrayOfInt.value.toString()
27 }
28 }
29
30 Text {
31 text: arrayOfInt.value.toString()
32 }
33
34 Text {
35 id: text2
36 objectName: "text2"
37 }
38
15}39}
40
1641
=== removed file 'tests/test_qtquick2.qml'
--- tests/test_qtquick2.qml 2013-03-08 16:59:19 +0000
+++ tests/test_qtquick2.qml 1970-01-01 00:00:00 +0000
@@ -1,15 +0,0 @@
1import QtQuick 2.0
2import Dee 3.0
3
4ListView {
5 width: 200
6 height: 200
7 delegate: Text {
8 x: 66
9 y: 93
10 text: column_4
11 }
12 model: DeeListModel {
13 name: "com.canonical.Unity.Lens.applications.T1313498309.Results"
14 }
15}
160
=== added file 'tests/tst_deelistmodel.qml'
--- tests/tst_deelistmodel.qml 1970-01-01 00:00:00 +0000
+++ tests/tst_deelistmodel.qml 2013-03-08 16:59:20 +0000
@@ -0,0 +1,79 @@
1import QtQuick 2.0
2import QtTest 1.0
3import Dee 3.0
4
5TestCase {
6 name: "DeeListModel"
7
8 function test_1_initialModel () {
9 compare(myModel.count, 0)
10 compare(myView.count, 0)
11 }
12
13 function test_1_modelAssigned () {
14 myModel.name = "com.dee.qml.model.test1"
15 countChanged.wait()
16 compare(myModel.name, "com.dee.qml.model.test1")
17 compare(myModel.count, 0)
18 compare(myView.count, 0)
19 }
20
21 function test_2_modelAppend () {
22 myModel.name = "com.dee.qml.model.test2"
23 countChanged.wait()
24 compare(myModel.count, 0, "model empty")
25 myModel.append("[5.95,\"Pizza\"]")
26 countChanged.wait()
27 compare(myModel.count, 1, "model: one row more")
28 compare(myView.count, myModel.count, "model count matches listview")
29 }
30
31 function test_3_modelRemove () {
32 myModel.name = "com.dee.qml.model.test3"
33 countChanged.wait()
34 compare(myModel.count, 0, "model empty")
35 myModel.append("[0.99,\"Grean Tea, Sencha\"]")
36 myModel.append("[0.89,\"Black Tea, Oolong\"]")
37 countChanged.wait()
38 compare(myModel.count, 2, "two rows more")
39 compare(myView.count, myModel.count, "model count matches listview")
40 myModel.remove(0)
41 countChanged.wait()
42 compare(myModel.count, 1, "one row less")
43 }
44
45 function test_4_roleDefined () {
46 myModel.name = "com.dee.qml.model.test4"
47 countChanged.wait()
48 compare(myModel.count, 0, "model empty")
49 myModel.append("[5.95,\"Pizza\"]")
50 countChanged.wait()
51 compare(myModel.count, 1, "one row more")
52 }
53
54 SignalSpy {
55 id: countChanged
56 target: myModel
57 signalName: "countChanged"
58 }
59
60 ListView {
61 id: myView
62
63 width: 200
64 height: 200
65 delegate: Text {
66 id: myTextDelegate
67 x: 66
68 y: 93
69 text: "" // column_0
70 }
71
72 model: DeeListModel {
73 id: myModel
74 name: "com.dee.qml.model.test0"
75 property string myColumnValue: ""
76 }
77 }
78}
79
080
=== added file 'tests/tst_deevarianttext.qml'
--- tests/tst_deevarianttext.qml 1970-01-01 00:00:00 +0000
+++ tests/tst_deevarianttext.qml 2013-03-08 16:59:20 +0000
@@ -0,0 +1,60 @@
1import QtQuick 2.0
2import QtTest 1.0
3import Dee 3.0
4
5TestCase {
6 name: "DeeVariantText"
7
8 function test_1_initialText () {
9 compare(arrayOfStr.value.toString(),"bar,foo")
10 }
11
12 function test_3_textAssigned () {
13 arrayOfInt.text = "[[4, 1.1, .1], [0, 0.3, 3.6]]"
14 compare(arrayOfInt.value.toString(),"4,1.1,0.1,0,0.3,3.6")
15 }
16
17 function test_4_textChanged () {
18 arrayOfInt.didChange = false
19 arrayOfInt.text = "@ams [nothing]"
20 compare(arrayOfInt.didChange,true)
21 }
22
23 function test_5_textNotChanged () {
24 arrayOfInt.text = "@ams [nothing]"
25 arrayOfInt.didChange = false
26 arrayOfInt.text = "@ams [nothing]"
27 compare(arrayOfInt.didChange,false)
28 }
29
30 function test_6_invalid () {
31 arrayOfInt.text = "[nothing]"
32 compare(arrayOfInt.value,undefined)
33 }
34
35 function test_7_unicode () {
36 arrayOfStr.text = "[\"١٢٣٤٥٦٧٨٩٠\", \"道具箱\", \"Котята\"]"
37 compare(arrayOfStr.value.toString(),"١٢٣٤٥٦٧٨٩٠,道具箱,Котята")
38 }
39
40 function test_8_gvariant () {
41 arrayOfStr.text = "[<\"test\">, <\"test2\">]"
42 compare(arrayOfStr.value.toString(), "test,test2")
43 }
44
45 DeeVariantText {
46 id: arrayOfStr
47 text: "[\"bar\", \"foo\"]"
48 }
49
50 DeeVariantText {
51 id: arrayOfInt
52 text: "[[1, 2, 3], [4, 5, 6]]"
53 property bool didChange: false
54 onTextChanged: {
55 didChange = true
56 }
57 }
58
59}
60

Subscribers

People subscribed via source and target branches