Merge lp:~mhr3/dee-qt/changeset-support into lp:dee-qt

Proposed by Michal Hruby
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 89
Merged at revision: 83
Proposed branch: lp:~mhr3/dee-qt/changeset-support
Merge into: lp:dee-qt
Diff against target: 1133 lines (+894/-37)
9 files modified
CMakeLists.txt (+7/-2)
cmake/COPYING-CMAKE-SCRIPTS (+22/-0)
cmake/Coverage.cmake (+37/-0)
debian/changelog (+6/-0)
deelistmodel.cpp (+174/-25)
tests/CMakeLists.txt (+10/-1)
tests/conversiontest.cpp (+59/-6)
tests/deelistmodeltest.cpp (+5/-3)
tests/signaltest.cpp (+574/-0)
To merge this branch: bzr merge lp:~mhr3/dee-qt/changeset-support
Reviewer Review Type Date Requested Status
Paweł Stołowski (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Review via email: mp+185042@code.launchpad.net

Commit message

Takes advantage of added changeset API in dee 1.2.7 to map more naturally to Qt's way of dealing with changes to models.

Description of the change

Takes advantage of added changeset API in dee 1.2.7 to map more naturally to Qt's way of dealing with changes to models.

Also added support for generating coverage and added a bunch of tests to bump the coverage number to >95% (from ~50%).

Requires lp:~mhr3/dee/add-changesets

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

357 + if (model->d->m_changesetInProgress) {

Shouldn't this say "if (!model->d->m_changesetInProgress) {"?

review: Needs Information
Revision history for this message
Paweł Stołowski (stolowski) wrote :

221 + // have the "extra" row from the addition, so we need to hide it
Can you enhance this comment a bit - how is "hiding" actually achieved here?

230 + m_count += m_changesetRowEnd - m_changesetRowStart + 1;
241 + m_count -= m_changesetRowEnd - m_changesetRowStart + 1;

Why "+1"

review: Needs Information
lp:~mhr3/dee-qt/changeset-support updated
89. By Michal Hruby

Explain things better, split up the flushing into separate method

Revision history for this message
Michal Hruby (mhr3) wrote :

> 357 + if (model->d->m_changesetInProgress) {
>
> Shouldn't this say "if (!model->d->m_changesetInProgress) {"?

No, this is callback for row-changed, in case of transaction we need to flush everything we received so far.

Revision history for this message
Michal Hruby (mhr3) wrote :

> 221 + // have the "extra" row from the addition, so we need to hide
> it
> Can you enhance this comment a bit - how is "hiding" actually achieved here?

Updated the comments, hopefully it's more clear now.

> 230 + m_count += m_changesetRowEnd - m_changesetRowStart + 1;
> 241 + m_count -= m_changesetRowEnd - m_changesetRowStart + 1;
>
> Why "+1"

Because a single insert is beginInsertRows(0, 0) => ie rowStart = 0, rowEnd = 0; count += 0 - 0 + 1;
For insertion of two rows: beginInsertRows(0, 1) => rowStart = 0, rowEnd = 1; count += 1 - 0 + 1;

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Paweł Stołowski (stolowski) wrote :

398 + // there's an extra row in the backend, skip it by incrementing
399 + row++;
400 + }
401 + } else {
402 + if (d->m_rowBeingRemoved >= 0 && row >= d->m_rowBeingRemoved) {
403 + // we need to skip the about-to-be-removed row
404 + row++;

Why are we skipping only one row? The changeset may span multiple rows, shouldn't this be taken into account?

review: Needs Information
Revision history for this message
Michal Hruby (mhr3) wrote :

When processing a transaction/changeset the row-* signals from Dee are used as synchronization points and since the Qt model signals are emitted during the transaction, there's really always just one row that needs to be skipped.

The primary thing here is that the skipping only happens when inside the row-* callback.

Consider how the transaction looks like in pseudocode:

// starting inside dee
var rows = dbus_signal.get_rows_iter();
while (rows.has_next())
{
  row = rows.next();
  var row_signal = this.submit_change(row, out iter);
  this.emit(row_signal, iter, () =>
  {
    // inside the signal emission we get into the DeeListModel plugin code
    if (consecutive_changes(iter)) processChange(...);
    else flushChangesAndProcessChange(() =>
    {
      beginRowsInserted(begin, end);
      // now qt will emit rowsInserted() or rowsRemoved()
      endRowsInserted(begin, end, () =>
      {
        // view code that will read the model
        for (int i = begin; i < end; i++)
          model.data(i, someRole); // at this point we need to skip the row that's currently being added (current iter, always just one)
      });
    });
  });
}

Of course the situation is different when the entire transaction is consecutive:
// starting inside dee
var rows = dbus_signal.get_rows_iter();
while (rows.has_next())
{
  row = rows.next();
  var row_signal = this.submit_change(row, out iter);
  this.emit(row_signal, iter, () => { processChange(...); }
}

this.emit(changeset_finished, () =>
{
  // inside the signal emission we get into the DeeListModel plugin code
  flushChanges(() =>
  {
    beginRowsInserted(begin, end);
    // now qt will emit rowsInserted() or rowsRemoved()
    endRowsInserted(begin, end, () =>
    {
      // view code that will read the model
      for (int i = begin; i < end; i++)
        model.data(i, someRole); // no need to skip anything, models are in sync
    });
  });
});

Revision history for this message
Paweł Stołowski (stolowski) wrote :

Nice stuff! Thanks for explanations!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2013-07-25 14:23:11 +0000
+++ CMakeLists.txt 2013-09-19 17:47:15 +0000
@@ -2,7 +2,7 @@
2cmake_minimum_required(VERSION 2.8.6)2cmake_minimum_required(VERSION 2.8.6)
33
4set(SONAME 3)4set(SONAME 3)
5set(VERSION 3.2)5set(VERSION 3.3)
6set(SOVERSION 3.0.0)6set(SOVERSION 3.0.0)
77
8# Instruct CMake to run moc automatically when needed.8# Instruct CMake to run moc automatically when needed.
@@ -10,6 +10,11 @@
1010
11# Dependencies11# Dependencies
12include(FindPkgConfig)12include(FindPkgConfig)
13
14set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake" "${CMAKE_MODULE_PATH}")
15
16include(Coverage)
17
13if (WITHQT5)18if (WITHQT5)
14 message("Building Qt5 version")19 message("Building Qt5 version")
1520
@@ -39,7 +44,7 @@
3944
40set(QT_DEE_PKGCONFIG_FILE lib${DEE_QT_LIBNAME}.pc)45set(QT_DEE_PKGCONFIG_FILE lib${DEE_QT_LIBNAME}.pc)
4146
42pkg_check_modules(DEE REQUIRED dee-1.0)47pkg_check_modules(DEE REQUIRED dee-1.0>=1.2.7)
4348
44# Build flags49# Build flags
45set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wundef -std=c++0x")50set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden -Wall -Wundef -std=c++0x")
4651
=== added directory 'cmake'
=== added file 'cmake/COPYING-CMAKE-SCRIPTS'
--- cmake/COPYING-CMAKE-SCRIPTS 1970-01-01 00:00:00 +0000
+++ cmake/COPYING-CMAKE-SCRIPTS 2013-09-19 17:47:15 +0000
@@ -0,0 +1,22 @@
1Redistribution and use in source and binary forms, with or without
2modification, are permitted provided that the following conditions
3are met:
4
51. Redistributions of source code must retain the copyright
6 notice, this list of conditions and the following disclaimer.
72. Redistributions in binary form must reproduce the copyright
8 notice, this list of conditions and the following disclaimer in the
9 documentation and/or other materials provided with the distribution.
103. The name of the author may not be used to endorse or promote products
11 derived from this software without specific prior written permission.
12
13THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023
=== added file 'cmake/Coverage.cmake'
--- cmake/Coverage.cmake 1970-01-01 00:00:00 +0000
+++ cmake/Coverage.cmake 2013-09-19 17:47:15 +0000
@@ -0,0 +1,37 @@
1if (CMAKE_BUILD_TYPE MATCHES coverage)
2 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
3 set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
4 set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} --coverage")
5 set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} --coverage")
6
7 find_program(GCOVR_EXECUTABLE gcovr HINTS ${GCOVR_ROOT} "${GCOVR_ROOT}/bin")
8 if (NOT GCOVR_EXECUTABLE)
9 message(STATUS "Gcovr binary was not found, can not generate XML coverage info.")
10 else ()
11 message(STATUS "Gcovr found, can generate XML coverage info.")
12 add_custom_target (coverage-xml
13 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
14 COMMAND "${GCOVR_EXECUTABLE}" --exclude="test.*" --exclude="obj.*" -x -r "${CMAKE_SOURCE_DIR}"
15 --object-directory=${CMAKE_BINARY_DIR} -o coverage.xml)
16 endif()
17
18 find_program(LCOV_EXECUTABLE lcov HINTS ${LCOV_ROOT} "${GCOVR_ROOT}/bin")
19 find_program(GENHTML_EXECUTABLE genhtml HINTS ${GENHTML_ROOT})
20 if (NOT LCOV_EXECUTABLE)
21 message(STATUS "Lcov binary was not found, can not generate HTML coverage info.")
22 else ()
23 if(NOT GENHTML_EXECUTABLE)
24 message(STATUS "Genthml binary not found, can not generate HTML coverage info.")
25 else()
26 message(STATUS "Lcov and genhtml found, can generate HTML coverage info.")
27 add_custom_target (coverage-html
28 WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
29 COMMAND "${LCOV_EXECUTABLE}" --capture --output-file "${CMAKE_BINARY_DIR}/coverage.info" --no-checksum --directory "${CMAKE_BINARY_DIR}"
30 COMMAND "${LCOV_EXECUTABLE}" --remove "${CMAKE_BINARY_DIR}/coverage.info" '/usr/*' --output-file "${CMAKE_BINARY_DIR}/coverage.info"
31 COMMAND "${LCOV_EXECUTABLE}" --remove "${CMAKE_BINARY_DIR}/coverage.info" '${CMAKE_BINARY_DIR}/moc_*' --output-file "${CMAKE_BINARY_DIR}/coverage.info"
32 COMMAND "${LCOV_EXECUTABLE}" --remove "${CMAKE_BINARY_DIR}/coverage.info" '${CMAKE_SOURCE_DIR}/tests/*' --output-file "${CMAKE_BINARY_DIR}/coverage.info"
33 COMMAND "${GENHTML_EXECUTABLE}" --prefix "${CMAKE_BINARY_DIR}" --output-directory coveragereport --title "Code Coverage" --legend --show-details coverage.info
34 )
35 endif()
36 endif()
37endif()
038
=== modified file 'debian/changelog'
--- debian/changelog 2013-08-21 16:18:56 +0000
+++ debian/changelog 2013-09-19 17:47:15 +0000
@@ -1,3 +1,9 @@
1dee-qt (3.3-0ubuntu1) UNRELEASED; urgency=low
2
3 * Take advantage of new changeset API in dee
4
5 -- Michal Hruby <michal.hruby@canonical.com> Tue, 10 Sep 2013 16:56:00 +0100
6
1dee-qt (3.2+13.10.20130821.1-0ubuntu1) saucy; urgency=low7dee-qt (3.2+13.10.20130821.1-0ubuntu1) saucy; urgency=low
28
3 [ Albert Astals ]9 [ Albert Astals ]
410
=== modified file 'deelistmodel.cpp'
--- deelistmodel.cpp 2013-08-21 11:03:07 +0000
+++ deelistmodel.cpp 2013-09-19 17:47:15 +0000
@@ -28,6 +28,19 @@
2828
29class DeeListModelPrivate {29class DeeListModelPrivate {
30public:30public:
31 enum ProcessingState
32 {
33 START,
34 ADDITIONS,
35 REMOVALS
36 };
37
38 enum ChangeType
39 {
40 ADDITION,
41 REMOVAL
42 };
43
31 DeeListModelPrivate(DeeListModel* parent);44 DeeListModelPrivate(DeeListModel* parent);
32 ~DeeListModelPrivate();45 ~DeeListModelPrivate();
3346
@@ -37,22 +50,43 @@
37 void createRoles();50 void createRoles();
38 bool synchronized() const;51 bool synchronized() const;
3952
53 void processChange(ChangeType changeType, int changePos);
54 void flushChanges();
55
40 /* GObject signal handlers for m_deeModel */56 /* GObject signal handlers for m_deeModel */
41 static void onSynchronizedChanged(GObject* emitter, GParamSpec *pspec, DeeListModel* model);57 static void onSynchronizedChanged(GObject* emitter, GParamSpec *pspec, DeeListModel* model);
42 static void onRowAdded(GObject* emitter, DeeModelIter* iter, DeeListModel* model);58 static void onRowAdded(GObject* emitter, DeeModelIter* iter, DeeListModel* model);
43 static void onRowRemoved(GObject* emitter, DeeModelIter* iter, DeeListModel* model);59 static void onRowRemoved(GObject* emitter, DeeModelIter* iter, DeeListModel* model);
44 static void onRowChanged(GObject* emitter, DeeModelIter* iter, DeeListModel* model);60 static void onRowChanged(GObject* emitter, DeeModelIter* iter, DeeListModel* model);
4561
62 static void onStartChangeset(DeeListModel* model);
63 static void onFinishChangeset(DeeListModel* model);
64
46 DeeListModel* m_parent;65 DeeListModel* m_parent;
47 DeeModel* m_deeModel;66 DeeModel* m_deeModel;
48 QString m_name;67 QString m_name;
49 int m_count;68 int m_count;
50 bool m_listeningSynchronized;69 bool m_listeningSynchronized;
51 QHash<int, QByteArray> m_roleNames;70 QHash<int, QByteArray> m_roleNames;
71 int m_rowBeingAdded;
52 int m_rowBeingRemoved;72 int m_rowBeingRemoved;
73 ProcessingState m_changesetState;
74 bool m_changesetInProgress;
75 int m_changesetRowStart;
76 int m_changesetRowEnd;
53};77};
5478
55DeeListModelPrivate::DeeListModelPrivate(DeeListModel* parent) : m_parent(parent), m_deeModel(NULL), m_count(0), m_listeningSynchronized(false), m_rowBeingRemoved(-1)79DeeListModelPrivate::DeeListModelPrivate(DeeListModel* parent)
80 : m_parent(parent)
81 , m_deeModel(NULL)
82 , m_count(0)
83 , m_listeningSynchronized(false)
84 , m_rowBeingAdded(-1)
85 , m_rowBeingRemoved(-1)
86 , m_changesetState(ProcessingState::START)
87 , m_changesetInProgress(false)
88 , m_changesetRowStart(-1)
89 , m_changesetRowEnd(-1)
56{90{
57}91}
5892
@@ -72,7 +106,10 @@
72 }106 }
73 g_object_disconnect(m_deeModel, "any_signal", G_CALLBACK(onRowAdded), m_parent,107 g_object_disconnect(m_deeModel, "any_signal", G_CALLBACK(onRowAdded), m_parent,
74 "any_signal", G_CALLBACK(onRowRemoved), m_parent,108 "any_signal", G_CALLBACK(onRowRemoved), m_parent,
75 "any_signal", G_CALLBACK(onRowChanged), m_parent, NULL);109 "any_signal", G_CALLBACK(onRowChanged), m_parent,
110 "any_signal", G_CALLBACK(onStartChangeset), m_parent,
111 "any_signal", G_CALLBACK(onFinishChangeset), m_parent,
112 NULL);
76113
77 g_object_unref(m_deeModel);114 g_object_unref(m_deeModel);
78 m_deeModel = NULL;115 m_deeModel = NULL;
@@ -104,6 +141,8 @@
104 g_signal_connect(m_deeModel, "row-added", G_CALLBACK(onRowAdded), m_parent);141 g_signal_connect(m_deeModel, "row-added", G_CALLBACK(onRowAdded), m_parent);
105 g_signal_connect(m_deeModel, "row-removed", G_CALLBACK(onRowRemoved), m_parent);142 g_signal_connect(m_deeModel, "row-removed", G_CALLBACK(onRowRemoved), m_parent);
106 g_signal_connect(m_deeModel, "row-changed", G_CALLBACK(onRowChanged), m_parent);143 g_signal_connect(m_deeModel, "row-changed", G_CALLBACK(onRowChanged), m_parent);
144 g_signal_connect_swapped(m_deeModel, "changeset-started", G_CALLBACK(onStartChangeset), m_parent);
145 g_signal_connect_swapped(m_deeModel, "changeset-finished", G_CALLBACK(onFinishChangeset), m_parent);
107 if (synchronized())146 if (synchronized())
108 {147 {
109 createRoles();148 createRoles();
@@ -134,6 +173,90 @@
134}173}
135174
136void175void
176DeeListModelPrivate::flushChanges()
177{
178 bool countChanged = false;
179 // The problem we're facing here is that the signals emitted by DeeListModel are not completely
180 // in sync with the backend DeeModel - Qt will likely call our data() method right after calling
181 // end{Insert|Remove}Rows() and the backend model might still have extra rows, because we're
182 // inside onRowRemoved/onRowAdded callback
183 // See the data() method for more details
184 if (m_changesetState == ProcessingState::ADDITIONS) {
185 /* Force emission of QAbstractItemModel::rowsInserted by calling
186 beginInsertRows and endInsertRows. Necessary because according to the
187 documentation:
188 "It can only be emitted by the QAbstractItemModel implementation, and
189 cannot be explicitly emitted in subclass code."
190 */
191 m_parent->beginInsertRows(QModelIndex(), m_changesetRowStart, m_changesetRowEnd);
192 m_count += m_changesetRowEnd - m_changesetRowStart + 1;
193 countChanged = true;
194 m_parent->endInsertRows();
195 } else if (m_changesetState == ProcessingState::REMOVALS) {
196 /* Force emission of QAbstractItemModel::rowsRemoved by calling
197 beginRemoveRows and endRemoveRows. Necessary because according to the
198 documentation:
199 "It can only be emitted by the QAbstractItemModel implementation, and
200 cannot be explicitly emitted in subclass code."
201 */
202 m_parent->beginRemoveRows(QModelIndex(), m_changesetRowStart, m_changesetRowEnd);
203 m_count -= m_changesetRowEnd - m_changesetRowStart + 1;
204 countChanged = true;
205 m_parent->endRemoveRows();
206 }
207
208 if (countChanged) Q_EMIT m_parent->countChanged();
209
210 m_changesetState = ProcessingState::START;
211 m_changesetRowStart = -1;
212 m_changesetRowEnd = -1;
213}
214
215void
216DeeListModelPrivate::processChange(ChangeType changeType, int changePos)
217{
218 /* flush if changeType doesn't match current processing state */
219 if (m_changesetState != ProcessingState::START &&
220 ((changeType == ChangeType::ADDITION && m_changesetState != ProcessingState::ADDITIONS) ||
221 (changeType == ChangeType::REMOVAL && m_changesetState != ProcessingState::REMOVALS))) {
222 flushChanges();
223 }
224
225 /* flush also if current changeType isn't consecutive:
226 * - consecutive additions are if changePos == m_changesetRowEnd + 1
227 * - consecutive removals are if changePos == m_changesetRowStart
228 */
229 if ((m_changesetState == ProcessingState::ADDITIONS && changePos != m_changesetRowEnd + 1) ||
230 (m_changesetState == ProcessingState::REMOVALS && changePos != m_changesetRowStart)) {
231 flushChanges();
232 }
233
234 switch (m_changesetState) {
235 case ProcessingState::START:
236 switch (changeType) {
237 case ChangeType::ADDITION:
238 m_changesetState = ProcessingState::ADDITIONS;
239 m_changesetRowStart = changePos;
240 m_changesetRowEnd = changePos;
241 break;
242 case ChangeType::REMOVAL:
243 m_changesetState = ProcessingState::REMOVALS;
244 m_changesetRowStart = changePos;
245 m_changesetRowEnd = changePos;
246 break;
247 default: break;
248 }
249 break;
250 case ProcessingState::ADDITIONS:
251 m_changesetRowEnd = changePos;
252 break;
253 case ProcessingState::REMOVALS:
254 m_changesetRowEnd++;
255 break;
256 }
257}
258
259void
137DeeListModelPrivate::createRoles()260DeeListModelPrivate::createRoles()
138{261{
139 if (m_deeModel == NULL) {262 if (m_deeModel == NULL) {
@@ -182,16 +305,14 @@
182 }305 }
183306
184 gint position = dee_model_get_position(model->d->m_deeModel, iter);307 gint position = dee_model_get_position(model->d->m_deeModel, iter);
185 /* Force emission of QAbstractItemModel::rowsInserted by calling308
186 beginInsertRows and endInsertRows. Necessary because according to the309 /* if we're inside transaction, we'll consider this row hidden */
187 documentation:310 model->d->m_rowBeingAdded = position;
188 "It can only be emitted by the QAbstractItemModel implementation, and311 model->d->processChange(ChangeType::ADDITION, position);
189 cannot be explicitly emitted in subclass code."312 if (!model->d->m_changesetInProgress) {
190 */313 model->d->flushChanges();
191 model->beginInsertRows(QModelIndex(), position, position);314 }
192 model->d->m_count++;315 model->d->m_rowBeingAdded = -1;
193 model->endInsertRows();
194 Q_EMIT model->countChanged();
195}316}
196317
197void318void
@@ -204,22 +325,17 @@
204 }325 }
205326
206 /* Note that at this point the row is still present and valid in the DeeModel.327 /* Note that at this point the row is still present and valid in the DeeModel.
207 Therefore the value returned by dee_model_get_n_columns() might not be328 Therefore the value returned by dee_model_get_n_rows() might not be
208 what one would expect.329 what one would expect.
209 See Dee's dee_sequence_model_remove() method.330 See Dee's dee_sequence_model_remove() method.
210 */331 */
211 gint position = dee_model_get_position(model->d->m_deeModel, iter);332 gint position = dee_model_get_position(model->d->m_deeModel, iter);
212 /* Force emission of QAbstractItemModel::rowsRemoved by calling333
213 beginRemoveRows and endRemoveRows. Necessary because according to the
214 documentation:
215 "It can only be emitted by the QAbstractItemModel implementation, and
216 cannot be explicitly emitted in subclass code."
217 */
218 model->beginRemoveRows(QModelIndex(), position, position);
219 model->d->m_rowBeingRemoved = position;334 model->d->m_rowBeingRemoved = position;
220 model->d->m_count--;335 model->d->processChange(ChangeType::REMOVAL, position);
221 model->endRemoveRows();336 if (!model->d->m_changesetInProgress) {
222 Q_EMIT model->countChanged();337 model->d->flushChanges();
338 }
223 model->d->m_rowBeingRemoved = -1;339 model->d->m_rowBeingRemoved = -1;
224}340}
225341
@@ -232,11 +348,30 @@
232 return;348 return;
233 }349 }
234350
351 /* If we're inside a transaction we need to flush the currently queued
352 * changes and emit the dataChanged signal after that */
353 if (model->d->m_changesetInProgress) {
354 model->d->flushChanges();
355 }
356
235 gint position = dee_model_get_position(model->d->m_deeModel, iter);357 gint position = dee_model_get_position(model->d->m_deeModel, iter);
236 QModelIndex index = model->index(position);358 QModelIndex index = model->index(position);
237 Q_EMIT model->dataChanged(index, index);359 Q_EMIT model->dataChanged(index, index);
238}360}
239361
362void
363DeeListModelPrivate::onStartChangeset(DeeListModel* model)
364{
365 model->d->m_changesetInProgress = true;
366}
367
368void
369DeeListModelPrivate::onFinishChangeset(DeeListModel* model)
370{
371 model->d->flushChanges();
372 model->d->m_changesetInProgress = false;
373}
374
240375
241376
242DeeListModel::DeeListModel(QObject *parent) :377DeeListModel::DeeListModel(QObject *parent) :
@@ -320,8 +455,22 @@
320 DeeModelIter* iter;455 DeeModelIter* iter;
321456
322 int row = index.row();457 int row = index.row();
323 if (d->m_rowBeingRemoved >= 0 && row >= d->m_rowBeingRemoved) {458 // we're inside the row-{added|removed} callback and the backend model still has the removed row
324 row++;459 // (in case of row-removed) or already has the added row (in case of row-added), there are two cases:
460 // 1) inside a transaction, we need to hide about-to-be-added row (because we just flushed changes unrelated
461 // to this addition)
462 // 2) outside of transaction, we need to hide about-to-be-removed row (because we're flushing right after
463 // receiving the signal)
464 if (d->m_changesetInProgress) {
465 if (d->m_rowBeingAdded >= 0 && row >= d->m_rowBeingAdded) {
466 // there's an extra row in the backend, skip it by incrementing
467 row++;
468 }
469 } else {
470 if (d->m_rowBeingRemoved >= 0 && row >= d->m_rowBeingRemoved) {
471 // we need to skip the about-to-be-removed row
472 row++;
473 }
325 }474 }
326475
327 iter = dee_model_get_iter_at_row(d->m_deeModel, row);476 iter = dee_model_get_iter_at_row(d->m_deeModel, row);
328477
=== modified file 'tests/CMakeLists.txt'
--- tests/CMakeLists.txt 2012-11-30 14:51:06 +0000
+++ tests/CMakeLists.txt 2013-09-19 17:47:15 +0000
@@ -15,9 +15,18 @@
15add_executable(conversiontest conversiontest.cpp)15add_executable(conversiontest conversiontest.cpp)
16target_link_libraries(conversiontest ${OUR_QT_TEST_LIB} ${DEE_QT_LIBNAME})16target_link_libraries(conversiontest ${OUR_QT_TEST_LIB} ${DEE_QT_LIBNAME})
17set_target_properties(conversiontest PROPERTIES COMPILE_FLAGS -fPIC)17set_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")18add_test(NAME conversiontest COMMAND "${CMAKE_CURRENT_BINARY_DIR}/conversiontest" "-o" "${CMAKE_CURRENT_BINARY_DIR}/conversiontest-xunit.xml,xunitxml" "-o" "-,txt")
19set_property(TEST conversiontest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")19set_property(TEST conversiontest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")
2020
21if (WITHQT5)
22 # the test is using signal connections with lambdas, only available in Qt5
23 add_executable(signaltest signaltest.cpp)
24 target_link_libraries(signaltest ${OUR_QT_TEST_LIB} ${DEE_QT_LIBNAME})
25 set_target_properties(signaltest PROPERTIES COMPILE_FLAGS -fPIC)
26 add_test(NAME signaltest COMMAND "${CMAKE_CURRENT_BINARY_DIR}/signaltest" "-o" "${CMAKE_CURRENT_BINARY_DIR}/signaltest-xunit.xml,xunitxml" "-o" "-,txt")
27 set_property(TEST signaltest PROPERTY ENVIRONMENT "LD_LIBRARY_PATH=.")
28endif ()
29
21add_executable(test-helper test-helper.cpp)30add_executable(test-helper test-helper.cpp)
22target_link_libraries(test-helper ${OUR_QT_CORE_LIB} ${OUR_QT_DBUS_LIB} ${DEE_LDFLAGS})31target_link_libraries(test-helper ${OUR_QT_CORE_LIB} ${OUR_QT_DBUS_LIB} ${DEE_LDFLAGS})
23set_target_properties(test-helper PROPERTIES COMPILE_FLAGS -fPIC)32set_target_properties(test-helper PROPERTIES COMPILE_FLAGS -fPIC)
2433
=== modified file 'tests/conversiontest.cpp'
--- tests/conversiontest.cpp 2013-06-21 17:12:07 +0000
+++ tests/conversiontest.cpp 2013-09-19 17:47:15 +0000
@@ -31,15 +31,11 @@
31 g_type_init();31 g_type_init();
32 }32 }
3333
34 void GVariantToQVariantConversionTest()34 void ModelQVariantConversionTest()
35 {35 {
36 DeeModel* model = dee_shared_model_new("com.deeqt.conversiontest");36 DeeModel* model = dee_sequence_model_new();
37 dee_model_set_schema(model, "b", "y", "n", "q", "i", "u", "x", "t", "d", "s", "a(ii)", "a{sv}", NULL);37 dee_model_set_schema(model, "b", "y", "n", "q", "i", "u", "x", "t", "d", "s", "a(ii)", "a{sv}", NULL);
3838
39 // Doc says we need to be synchronized before doing anything
40 while(!dee_shared_model_is_synchronized(DEE_SHARED_MODEL(model)))
41 qApp->processEvents();
42
43 GVariant **tuples = g_new(GVariant *, 2);39 GVariant **tuples = g_new(GVariant *, 2);
4440
45 GVariant **t1 = g_new(GVariant *, 2);41 GVariant **t1 = g_new(GVariant *, 2);
@@ -68,6 +64,7 @@
68 QCOMPARE(model_qt.count(), 0);64 QCOMPARE(model_qt.count(), 0);
6965
70 model_qt.setModel(model);66 model_qt.setModel(model);
67 QCOMPARE(model_qt.synchronized(), true);
71 QCOMPARE(model_qt.count(), 1);68 QCOMPARE(model_qt.count(), 1);
7269
73 const QModelIndex row0Index = model_qt.index(0, 0);70 const QModelIndex row0Index = model_qt.index(0, 0);
@@ -99,6 +96,62 @@
99 g_free(t2);96 g_free(t2);
100 g_object_unref(model);97 g_object_unref(model);
101 }98 }
99
100 void GVariantToQVariantConversionTest()
101 {
102 GVariant **tuples = g_new(GVariant *, 2);
103
104 GVariant **t1 = g_new(GVariant *, 2);
105 t1[0] = g_variant_new_int32(1);
106 t1[1] = g_variant_new_int32(2);
107 tuples[0] = g_variant_new_tuple(t1, 2);
108
109 GVariant **t2 = g_new(GVariant *, 2);
110 t2[0] = g_variant_new_int32(3);
111 t2[1] = g_variant_new_int32(4);
112 tuples[1] = g_variant_new_tuple(t2, 2);
113
114 GVariant* array_of_tuples = g_variant_new_array(((const GVariantType *) "(ii)"), tuples, 2);
115
116 QVariant variant(DeeListModel::VariantForData(array_of_tuples));
117
118 QList< QVariant > expected_array;
119 QList< QVariant > tuple1, tuple2;
120 tuple1 << 1 << 2;
121 tuple2 << 3 << 4;
122 expected_array << QVariant(tuple1) << QVariant(tuple2);
123 QCOMPARE(variant, QVariant(expected_array));
124
125 g_free(t1);
126 g_free(t2);
127 g_free(tuples);
128
129 GVariant* nested_variant = g_variant_new_variant(g_variant_new_string("nested"));
130 variant = DeeListModel::VariantForData(nested_variant);
131 QCOMPARE(variant.toString(), QString("nested"));
132 }
133
134 void QVariantToGVariantConversionTest()
135 {
136 int cnt = 0;
137 GVariant **children = new GVariant*[8];
138 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("int"), g_variant_new_variant(g_variant_new_int32(-82)));
139 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("bool"), g_variant_new_variant(g_variant_new_boolean(TRUE)));
140 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("string"), g_variant_new_variant(g_variant_new_string("foo")));
141 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("uint"), g_variant_new_variant(g_variant_new_uint32(90)));
142 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("int64"), g_variant_new_variant(g_variant_new_int64(-401230)));
143 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("uint64"), g_variant_new_variant(g_variant_new_uint64(401230)));
144 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("double"), g_variant_new_variant(g_variant_new_double(3.1415)));
145 children[cnt++] = g_variant_new_dict_entry(g_variant_new_string("int-array"), g_variant_new_variant(g_variant_new_parsed("[1, 2, 3]")));
146 GVariant* hints = g_variant_new_array(G_VARIANT_TYPE("{sv}"), children, cnt);
147
148 QVariant variant(DeeListModel::VariantForData(hints));
149 GVariant* reconstructed_gvariant = DeeListModel::DataFromVariant(variant);
150
151 // the ordering in the variant might be different, so don't compare directly
152 QVERIFY(g_variant_type_equal(g_variant_get_type(hints), g_variant_get_type(reconstructed_gvariant)));
153 QCOMPARE(g_variant_n_children(hints), g_variant_n_children(reconstructed_gvariant));
154 }
102};155};
103156
104QTEST_MAIN(ConversionTest)157QTEST_MAIN(ConversionTest)
105158
=== modified file 'tests/deelistmodeltest.cpp'
--- tests/deelistmodeltest.cpp 2012-11-30 12:56:33 +0000
+++ tests/deelistmodeltest.cpp 2013-09-19 17:47:15 +0000
@@ -87,12 +87,10 @@
8787
88 void setExistingModelTest()88 void setExistingModelTest()
89 {89 {
90 DeeModel* model = dee_shared_model_new("com.deeqt.test.model");
91
92 DeeListModel model_qt;90 DeeListModel model_qt;
93 QCOMPARE(model_qt.count(), 0);91 QCOMPARE(model_qt.count(), 0);
9492
95 model_qt.setModel(model);93 model_qt.setName("com.deeqt.test.model");
96 QCOMPARE(model_qt.synchronized(), false);94 QCOMPARE(model_qt.synchronized(), false);
9795
98 while(!model_qt.synchronized())96 while(!model_qt.synchronized())
@@ -101,6 +99,10 @@
101 QCOMPARE(model_qt.synchronized(), true);99 QCOMPARE(model_qt.synchronized(), true);
102 QCOMPARE(model_qt.roleNames().count(), 1);100 QCOMPARE(model_qt.roleNames().count(), 1);
103 QCOMPARE(model_qt.roleNames()[0], QByteArray("column_0"));101 QCOMPARE(model_qt.roleNames()[0], QByteArray("column_0"));
102
103 model_qt.setName("");
104 QCOMPARE(model_qt.synchronized(), false);
105 QCOMPARE(model_qt.rowCount(), 0);
104 }106 }
105 107
106 void cleanupTestCase()108 void cleanupTestCase()
107109
=== added file 'tests/signaltest.cpp'
--- tests/signaltest.cpp 1970-01-01 00:00:00 +0000
+++ tests/signaltest.cpp 2013-09-19 17:47:15 +0000
@@ -0,0 +1,574 @@
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 "deelistmodel.h"
21
22#include <dee.h>
23
24class DeeListModelTest : public QObject
25{
26 Q_OBJECT
27
28private Q_SLOTS:
29 void initTestCase()
30 {
31 }
32
33 void synchronizationTest()
34 {
35 DeeModel* model = dee_sequence_model_new();
36 dee_model_set_schema(model, "s", "i", NULL);
37
38 DeeListModel model_qt;
39 QCOMPARE(model_qt.rowCount(), 0);
40 QCOMPARE(model_qt.count(), 0);
41
42 model_qt.setModel(model);
43 QCOMPARE(model_qt.synchronized(), true);
44 QVERIFY(model_qt.name().isNull());
45
46 model_qt.setModel(NULL);
47 QCOMPARE(model_qt.synchronized(), false);
48 QCOMPARE(model_qt.rowCount(), 0);
49 QCOMPARE(model_qt.count(), 0);
50 }
51
52 void emitBasicSignalsTest()
53 {
54 DeeModel* model = dee_sequence_model_new();
55 dee_model_set_schema(model, "s", "i", NULL);
56
57 DeeListModel model_qt;
58 model_qt.setModel(model);
59
60 int num_insertions = 0;
61
62 connect(&model_qt, &QAbstractItemModel::rowsInserted, [&num_insertions] (const QModelIndex &parent, int start, int end) {
63 num_insertions++;
64 });
65
66 dee_model_append(model, "foo", 5);
67
68 QCOMPARE(num_insertions, 1);
69 QCOMPARE(model_qt.count(), 1);
70 QCOMPARE(model_qt.rowCount(), 1);
71 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
72 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
73
74 int num_removals = 0;
75 connect(&model_qt, &QAbstractItemModel::rowsRemoved, [&num_removals] (const QModelIndex &parent, int start, int end) {
76 num_removals++;
77 });
78
79 dee_model_remove(model, dee_model_get_first_iter(model));
80
81 QCOMPARE(num_removals, 1);
82 QCOMPARE(model_qt.count(), 0);
83 QCOMPARE(model_qt.rowCount(), 0);
84 QCOMPARE(num_insertions, 1);
85 }
86
87 void emitMultipleSignalsTest()
88 {
89 DeeModel* model = dee_sequence_model_new();
90 dee_model_set_schema(model, "s", "i", NULL);
91
92 DeeListModel model_qt;
93 model_qt.setModel(model);
94
95 int num_insertions = 0;
96
97 connect(&model_qt, &QAbstractItemModel::rowsInserted, [&num_insertions] (const QModelIndex &parent, int start, int end) {
98 num_insertions++;
99 });
100
101 dee_model_append(model, "foo", 5);
102 QCOMPARE(num_insertions, 1);
103 QCOMPARE(model_qt.count(), 1);
104 QCOMPARE(model_qt.rowCount(), 1);
105 dee_model_append(model, "bar", 6);
106 QCOMPARE(num_insertions, 2);
107 QCOMPARE(model_qt.count(), 2);
108 QCOMPARE(model_qt.rowCount(), 2);
109 dee_model_append(model, "baz", 7);
110 QCOMPARE(num_insertions, 3);
111 QCOMPARE(model_qt.count(), 3);
112 QCOMPARE(model_qt.rowCount(), 3);
113 dee_model_append(model, "qoo", 8);
114
115 QCOMPARE(num_insertions, 4);
116 QCOMPARE(model_qt.count(), 4);
117 QCOMPARE(model_qt.rowCount(), 4);
118
119 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
120 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
121 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("bar"));
122 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 6);
123 QCOMPARE(model_qt.data(model_qt.index(2, 0), 0).toString(), QString("baz"));
124 QCOMPARE(model_qt.data(model_qt.index(2, 0), 1).toInt(), 7);
125 QCOMPARE(model_qt.data(model_qt.index(3, 0), 0).toString(), QString("qoo"));
126 QCOMPARE(model_qt.data(model_qt.index(3, 0), 1).toInt(), 8);
127
128 int num_removals = 0;
129 connect(&model_qt, &QAbstractItemModel::rowsRemoved, [&num_removals] (const QModelIndex &parent, int start, int end) {
130 num_removals++;
131 });
132
133 num_insertions = 0;
134
135 dee_model_remove(model, dee_model_get_first_iter(model));
136 QCOMPARE(num_removals, 1);
137 QCOMPARE(model_qt.count(), 3);
138 QCOMPARE(model_qt.rowCount(), 3);
139 dee_model_remove(model, dee_model_get_first_iter(model));
140 QCOMPARE(num_removals, 2);
141 QCOMPARE(model_qt.count(), 2);
142 QCOMPARE(model_qt.rowCount(), 2);
143 dee_model_remove(model, dee_model_get_first_iter(model));
144 QCOMPARE(num_removals, 3);
145 QCOMPARE(model_qt.count(), 1);
146 QCOMPARE(model_qt.rowCount(), 1);
147 dee_model_remove(model, dee_model_get_first_iter(model));
148 QCOMPARE(num_removals, 4);
149 QCOMPARE(model_qt.count(), 0);
150 QCOMPARE(model_qt.rowCount(), 0);
151 dee_model_clear(model);
152 QCOMPARE(num_removals, 4);
153 QCOMPARE(model_qt.count(), 0);
154 QCOMPARE(model_qt.rowCount(), 0);
155
156 QCOMPARE(num_insertions, 0);
157 }
158
159 void emitConsecutiveInsertsTest()
160 {
161 DeeModel* model = dee_sequence_model_new();
162 dee_model_set_schema(model, "s", "i", NULL);
163
164 DeeListModel model_qt;
165 model_qt.setModel(model);
166
167 int num_insertions = 0;
168
169 connect(&model_qt, &QAbstractItemModel::rowsInserted, [&num_insertions] (const QModelIndex &parent, int start, int end) {
170 num_insertions++;
171 });
172
173 dee_model_begin_changeset(model);
174
175 dee_model_append(model, "foo", 5);
176 dee_model_append(model, "bar", 6);
177 dee_model_append(model, "baz", 7);
178 dee_model_append(model, "qoo", 8);
179
180 QCOMPARE(num_insertions, 0);
181
182 dee_model_end_changeset(model);
183
184 QCOMPARE(num_insertions, 1);
185 QCOMPARE(model_qt.count(), 4);
186 QCOMPARE(model_qt.rowCount(), 4);
187
188 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
189 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
190 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("bar"));
191 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 6);
192 QCOMPARE(model_qt.data(model_qt.index(2, 0), 0).toString(), QString("baz"));
193 QCOMPARE(model_qt.data(model_qt.index(2, 0), 1).toInt(), 7);
194 QCOMPARE(model_qt.data(model_qt.index(3, 0), 0).toString(), QString("qoo"));
195 QCOMPARE(model_qt.data(model_qt.index(3, 0), 1).toInt(), 8);
196
197 QVariantMap row2 = model_qt.get(2);
198 QCOMPARE(row2["column_0"].toString(), QString("baz"));
199 QCOMPARE(row2["column_1"].toInt(), 7);
200 }
201
202 void emitConsecutiveRemovalsTest()
203 {
204 DeeModel* model = dee_sequence_model_new();
205 dee_model_set_schema(model, "s", "i", NULL);
206
207 DeeListModel model_qt;
208 model_qt.setModel(model);
209
210 dee_model_append(model, "foo", 5);
211 dee_model_append(model, "bar", 6);
212 dee_model_append(model, "baz", 7);
213 dee_model_append(model, "qoo", 8);
214
215 QCOMPARE(model_qt.count(), 4);
216 QCOMPARE(model_qt.rowCount(), 4);
217
218 int num_removals = 0;
219
220 connect(&model_qt, &QAbstractItemModel::rowsRemoved, [&num_removals] (const QModelIndex &parent, int start, int end) {
221 num_removals++;
222 });
223
224 dee_model_begin_changeset(model);
225
226 dee_model_clear(model);
227
228 dee_model_end_changeset(model);
229
230 QCOMPARE(num_removals, 1);
231 QCOMPARE(model_qt.count(), 0);
232 QCOMPARE(model_qt.rowCount(), 0);
233 }
234
235 void emitConsecutiveInsertsWithChangeTest()
236 {
237 DeeModel* model = dee_sequence_model_new();
238 dee_model_set_schema(model, "s", "i", NULL);
239
240 DeeListModel model_qt;
241 model_qt.setModel(model);
242
243 int num_insertions = 0;
244
245 connect(&model_qt, &QAbstractItemModel::rowsInserted, [&num_insertions] (const QModelIndex &parent, int start, int end) {
246 num_insertions++;
247 });
248
249 dee_model_begin_changeset(model);
250
251 dee_model_append(model, "koo", -5);
252 dee_model_append(model, "bar", 6);
253 dee_model_append(model, "baz", 7);
254 dee_model_set(model, dee_model_get_first_iter(model), "foo", 5);
255 dee_model_append(model, "qoo", 8);
256
257 QCOMPARE(num_insertions, 1);
258 QCOMPARE(model_qt.count(), 3);
259 QCOMPARE(model_qt.rowCount(), 3);
260
261 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
262 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
263 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("bar"));
264 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 6);
265 QCOMPARE(model_qt.data(model_qt.index(2, 0), 0).toString(), QString("baz"));
266 QCOMPARE(model_qt.data(model_qt.index(2, 0), 1).toInt(), 7);
267
268 dee_model_end_changeset(model);
269
270 QCOMPARE(num_insertions, 2);
271 QCOMPARE(model_qt.count(), 4);
272 QCOMPARE(model_qt.rowCount(), 4);
273
274 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
275 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
276 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("bar"));
277 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 6);
278 QCOMPARE(model_qt.data(model_qt.index(2, 0), 0).toString(), QString("baz"));
279 QCOMPARE(model_qt.data(model_qt.index(2, 0), 1).toInt(), 7);
280 QCOMPARE(model_qt.data(model_qt.index(3, 0), 0).toString(), QString("qoo"));
281 QCOMPARE(model_qt.data(model_qt.index(3, 0), 1).toInt(), 8);
282 }
283
284 void emitSignalsMixedTest()
285 {
286 DeeModel* model = dee_sequence_model_new();
287 dee_model_set_schema(model, "s", "i", NULL);
288
289 DeeListModel model_qt;
290 model_qt.setModel(model);
291
292 int num_insertions = 0;
293 int num_removals = 0;
294
295 connect(&model_qt, &QAbstractItemModel::rowsInserted, [&num_insertions] (const QModelIndex &parent, int start, int end) {
296 num_insertions++;
297 });
298
299 connect(&model_qt, &QAbstractItemModel::rowsRemoved, [&num_removals] (const QModelIndex &parent, int start, int end) {
300 num_removals++;
301 });
302
303 dee_model_begin_changeset(model);
304
305 dee_model_append(model, "foo", 5);
306 dee_model_append(model, "bar", 6);
307 dee_model_append(model, "baz", 7);
308 dee_model_append(model, "qoo", 8);
309
310 dee_model_remove(model, dee_model_get_first_iter(model));
311 dee_model_remove(model, dee_model_get_first_iter(model));
312
313 dee_model_end_changeset(model);
314
315 QCOMPARE(num_insertions, 1);
316 QCOMPARE(num_removals, 1);
317 QCOMPARE(model_qt.count(), 2);
318 QCOMPARE(model_qt.rowCount(), 2);
319 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("baz"));
320 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 7);
321 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("qoo"));
322 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 8);
323
324 // continue with more changes that aren't consecutive
325 num_insertions = 0;
326 num_removals = 0;
327
328 dee_model_begin_changeset(model);
329
330 dee_model_append(model, "foo", 5);
331 dee_model_remove(model, dee_model_get_iter_at_row(model, 1));
332 dee_model_remove(model, dee_model_get_iter_at_row(model, 0));
333 dee_model_prepend(model, "qoo", 8);
334
335 dee_model_end_changeset(model);
336
337 QCOMPARE(num_insertions, 2);
338 QCOMPARE(num_removals, 2);
339 QCOMPARE(model_qt.count(), 2);
340 QCOMPARE(model_qt.rowCount(), 2);
341 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("qoo"));
342 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 8);
343 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("foo"));
344 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 5);
345
346 // and once again
347 num_insertions = 0;
348 num_removals = 0;
349
350 dee_model_begin_changeset(model);
351
352 dee_model_remove(model, dee_model_get_first_iter(model)); // ++ rem
353 dee_model_append(model, "foo", 5); // ++ ins
354 dee_model_clear(model); // ++ rem
355 dee_model_append(model, "baz", 7);
356 dee_model_append(model, "qoo", 8);
357 dee_model_prepend(model, "bar", 6);
358 dee_model_prepend(model, "foo", 5);
359
360 dee_model_end_changeset(model);
361
362 QCOMPARE(num_insertions, 4);
363 QCOMPARE(num_removals, 2);
364 QCOMPARE(model_qt.count(), 4);
365 QCOMPARE(model_qt.rowCount(), 4);
366 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
367 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
368 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("bar"));
369 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 6);
370 QCOMPARE(model_qt.data(model_qt.index(2, 0), 0).toString(), QString("baz"));
371 QCOMPARE(model_qt.data(model_qt.index(2, 0), 1).toInt(), 7);
372 QCOMPARE(model_qt.data(model_qt.index(3, 0), 0).toString(), QString("qoo"));
373 QCOMPARE(model_qt.data(model_qt.index(3, 0), 1).toInt(), 8);
374 }
375
376 void readDuringTransactionTest()
377 {
378 DeeModel* model = dee_sequence_model_new();
379 dee_model_set_schema(model, "s", "i", NULL);
380
381 DeeListModel model_qt;
382 model_qt.setModel(model);
383
384 int num_insertions = 0;
385 int num_removals = 0;
386 int state = 0;
387
388 connect(&model_qt, &QAbstractItemModel::rowsInserted, [&] (const QModelIndex &parent, int start, int end) {
389 num_insertions++;
390 QVERIFY(state >= 0 && state <= 2);
391 if (state == 0) {
392 QCOMPARE(start, 0);
393 QCOMPARE(end, 1);
394 QCOMPARE(model_qt.rowCount(), 2);
395 QCOMPARE(model_qt.count(), 2);
396 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
397 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
398 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("baz"));
399 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 7);
400 } else if (state == 1) {
401 QCOMPARE(start, 0);
402 QCOMPARE(end, 0);
403 QCOMPARE(model_qt.rowCount(), 3);
404 QCOMPARE(model_qt.count(), 3);
405 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("bar"));
406 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 6);
407 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("foo"));
408 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 5);
409 QCOMPARE(model_qt.data(model_qt.index(2, 0), 0).toString(), QString("baz"));
410 QCOMPARE(model_qt.data(model_qt.index(2, 0), 1).toInt(), 7);
411 } else if (state == 2) {
412 QCOMPARE(start, 1);
413 QCOMPARE(end, 1);
414 QCOMPARE(model_qt.rowCount(), 2);
415 QCOMPARE(model_qt.count(), 2);
416 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("baz"));
417 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 7);
418 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("qoo"));
419 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 8);
420 }
421 });
422
423 connect(&model_qt, &QAbstractItemModel::rowsRemoved, [&] (const QModelIndex &parent, int start, int end) {
424 num_removals++;
425 QVERIFY(state >= 2 && state <= 2);
426 if (state == 2) {
427 QCOMPARE(start, 0);
428 QCOMPARE(end, 1);
429 QCOMPARE(model_qt.rowCount(), 1);
430 QCOMPARE(model_qt.count(), 1);
431 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("baz"));
432 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 7);
433 }
434 });
435
436 dee_model_begin_changeset(model);
437
438 dee_model_append(model, "foo", 5);
439 dee_model_append(model, "baz", 7);
440 dee_model_prepend(model, "bar", 6);
441
442 state = 1;
443
444 dee_model_remove(model, dee_model_get_first_iter(model));
445 dee_model_remove(model, dee_model_get_first_iter(model));
446
447 state = 2;
448
449 dee_model_append(model, "qoo", 8);
450
451 dee_model_end_changeset(model);
452
453 QCOMPARE(num_insertions, 3);
454 QCOMPARE(num_removals, 1);
455 QCOMPARE(model_qt.count(), 2);
456 QCOMPARE(model_qt.rowCount(), 2);
457 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("baz"));
458 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 7);
459 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("qoo"));
460 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 8);
461 }
462
463 void readOutsideOfTransactionTest()
464 {
465 DeeModel* model = dee_sequence_model_new();
466 dee_model_set_schema(model, "s", "i", NULL);
467
468 DeeListModel model_qt;
469 model_qt.setModel(model);
470
471 int num_insertions = 0;
472 int num_removals = 0;
473 int state = 0;
474
475 connect(&model_qt, &QAbstractItemModel::rowsInserted, [&] (const QModelIndex &parent, int start, int end) {
476 num_insertions++;
477 QVERIFY((state >= 0 && state <= 2) || state == 5);
478 if (state == 0) {
479 QCOMPARE(start, 0);
480 QCOMPARE(end, 0);
481 QCOMPARE(model_qt.rowCount(), 1);
482 QCOMPARE(model_qt.count(), 1);
483 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
484 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
485 } else if (state == 1) {
486 QCOMPARE(start, 1);
487 QCOMPARE(end, 1);
488 QCOMPARE(model_qt.rowCount(), 2);
489 QCOMPARE(model_qt.count(), 2);
490 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
491 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
492 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("baz"));
493 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 7);
494 } else if (state == 2) {
495 QCOMPARE(start, 0);
496 QCOMPARE(end, 0);
497 QCOMPARE(model_qt.rowCount(), 3);
498 QCOMPARE(model_qt.count(), 3);
499 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("bar"));
500 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 6);
501 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("foo"));
502 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 5);
503 QCOMPARE(model_qt.data(model_qt.index(2, 0), 0).toString(), QString("baz"));
504 QCOMPARE(model_qt.data(model_qt.index(2, 0), 1).toInt(), 7);
505 } else if (state == 5) {
506 QCOMPARE(start, 1);
507 QCOMPARE(end, 1);
508 QCOMPARE(model_qt.rowCount(), 2);
509 QCOMPARE(model_qt.count(), 2);
510 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("baz"));
511 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 7);
512 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("qoo"));
513 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 8);
514 }
515 });
516
517 connect(&model_qt, &QAbstractItemModel::rowsRemoved, [&] (const QModelIndex &parent, int start, int end) {
518 num_removals++;
519 QVERIFY(state >= 3 && state <= 4);
520 if (state == 3) {
521 QCOMPARE(start, 0);
522 QCOMPARE(end, 0);
523 QCOMPARE(model_qt.rowCount(), 2);
524 QCOMPARE(model_qt.count(), 2);
525 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("foo"));
526 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 5);
527 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("baz"));
528 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 7);
529 } else if (state == 4) {
530 QCOMPARE(start, 0);
531 QCOMPARE(end, 0);
532 QCOMPARE(model_qt.rowCount(), 1);
533 QCOMPARE(model_qt.count(), 1);
534 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("baz"));
535 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 7);
536 }
537 });
538
539 dee_model_append(model, "foo", 5);
540
541 state = 1;
542
543 dee_model_append(model, "baz", 7);
544
545 state = 2;
546
547 dee_model_prepend(model, "bar", 6);
548
549 state = 3;
550
551 dee_model_remove(model, dee_model_get_first_iter(model));
552
553 state = 4;
554
555 dee_model_remove(model, dee_model_get_first_iter(model));
556
557 state = 5;
558
559 dee_model_append(model, "qoo", 8);
560
561 QCOMPARE(num_insertions, 4);
562 QCOMPARE(num_removals, 2);
563 QCOMPARE(model_qt.count(), 2);
564 QCOMPARE(model_qt.rowCount(), 2);
565 QCOMPARE(model_qt.data(model_qt.index(0, 0), 0).toString(), QString("baz"));
566 QCOMPARE(model_qt.data(model_qt.index(0, 0), 1).toInt(), 7);
567 QCOMPARE(model_qt.data(model_qt.index(1, 0), 0).toString(), QString("qoo"));
568 QCOMPARE(model_qt.data(model_qt.index(1, 0), 1).toInt(), 8);
569 }
570};
571
572QTEST_MAIN(DeeListModelTest)
573
574#include "signaltest.moc"

Subscribers

People subscribed via source and target branches

to all changes: