Merge lp:~mhr3/libunity/diff-models into lp:libunity

Proposed by Michal Hruby
Status: Merged
Approved by: Paweł Stołowski
Approved revision: 266
Merged at revision: 255
Proposed branch: lp:~mhr3/libunity/diff-models
Merge into: lp:libunity
Diff against target: 1780 lines (+1156/-278)
16 files modified
configure.ac (+5/-2)
debian/changelog (+6/-0)
debian/libunity9.symbols (+0/-150)
protocol/protocol-scope-interface.vala (+2/-1)
src/Makefile.am (+2/-1)
src/unity-aggregator-scope-private.vala (+13/-18)
src/unity-deprecated-scope-impl.vala (+17/-14)
src/unity-models.vala (+200/-0)
src/unity-scope-channel.vala (+48/-8)
src/unity-scope-dbus-impl.vala (+6/-12)
src/unity-scope-interface.vala (+1/-1)
src/unity-search.vala (+0/-71)
src/unity-utils.vala (+341/-0)
test/vala/Makefile.am (+4/-0)
test/vala/test-diff.vala (+507/-0)
test/vala/test-vala.vala (+4/-0)
To merge this branch: bzr merge lp:~mhr3/libunity/diff-models
Reviewer Review Type Date Requested Status
PS Jenkins bot (community) continuous-integration Approve
Paweł Stołowski (community) Approve
Review via email: mp+173012@code.launchpad.net

Commit message

Implement support for models that do diff between the last and current state to minimize the number of additions and removals.

Description of the change

Implement support for models that do diff between the last and current state to minimize the number of additions and removals to be able to provide more visually pleasing experience without the need for the shell to do this itself. This is needed because most scopes always clear the previous result set and re-add appropriate results, even though the results may not change.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Michał Sawicz (saviq) wrote :

This will be *awesome* :)

Question: is there a timer by which you batch the changes from the scopes and modify the model or?

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

It depends on the scope - ie right now they can control it. Most scopes will flush the changes when search finishes, a few do it in stages (files - recent files first, locate search a bit later), and for example home scope uses a timer.

But, we could change this to be managed centrally if needed.

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 :

568 + // consider uris the primary key
569 + return get_string (this_iter, ResultColumn.URI) ==
570 + target_model.get_string (target_iter, ResultColumn.URI);

I think we wanted to avoid this assumption in the new Unity shell? How about category + uri + title (+icon)?

review: Needs Fixing
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 :

This MP is a great improvement and seems to be working fine (and also unit tests prove it), but it's really hard to understand and judge the code...
Could you please at very least add docs to the main data structures used in the algorithm? Also, would it be feasible to break some bigger chunks of code (e.g. find_diag function) into smaller functions with descriptive names?

893 + int diags = x_set_length + y_set_length + 3;
895 + // allocate one big buffer for the forward and backward vectors
896 + real_diag = new int[diags * 2];
897 + // offset the pointers, cause we're sharing the same mem block,
898 + // plus the indices can be negative
914 + while (diags != 0)
915 + {
916 + diags /= 4;
917 + max_cost *= 2;
918 + }
919 + max_cost = int.max (max_cost, 256);

There are a few magic numbers above - can you add comments describing them?
Also, 896 and the comment in 897-898 look a bit scary, are we certain it's never accessing array out of bounds?

881 + int* f_diag;
882 + int* b_diag;
883 + int max_cost;
884 + uint8[] real_changes;
885 + uint8* x_changes;
886 + uint8* y_changes;

Hmm... I was told by my vala teacher ;) that pointers in vala are evil?...

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

> Also, would it be feasible to break some bigger chunks of code
> (e.g. find_diag function) into smaller functions with descriptive names?

I do agree that the find_diag function is hard to read, but... I'd rather not split it for 2 reasons:
1) this is the heart of the algorithm and therefore perf-critical
2) although some parts look like they're the same (the inner for loops), they're not, as they are operating on opposite sides of the edit matrix. Therefore IMO moving the parts into separate functions will not actually improve readability (suddenly you have 2 smaller function that look the same), otoh will have perf cost.

> 893 + int diags = x_set_length + y_set_length + 3;
> 895 + // allocate one big buffer for the forward and backward
> vectors
> 896 + real_diag = new int[diags * 2];
> 897 + // offset the pointers, cause we're sharing the same mem
> block,
> 898 + // plus the indices can be negative
> 914 + while (diags != 0)
> 915 + {
> 916 + diags /= 4;
> 917 + max_cost *= 2;
> 918 + }
> 919 + max_cost = int.max (max_cost, 256);
>
> There are a few magic numbers above - can you add comments describing them?

Does `diags = (x_set_length + 1 + y_set_length + 1) + 1;` look more readable?

> Also, 896 and the comment in 897-898 look a bit scary, are we certain it's
> never accessing array out of bounds?

I suppose we'll learn soon :)

>
> 881 + int* f_diag;
> 882 + int* b_diag;
> 883 + int max_cost;
> 884 + uint8[] real_changes;
> 885 + uint8* x_changes;
> 886 + uint8* y_changes;
>
> Hmm... I was told by my vala teacher ;) that pointers in vala are evil?...

They are, but you can't do pointer arithmetic on non-pointer types. FWIW they're not used to alloc and free memory.

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 :

Ok, let's get it in! Great work!

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
lp:~mhr3/libunity/diff-models updated
266. By Michal Hruby

Use DeeServer in tests which is hopefully less racy

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'configure.ac'
2--- configure.ac 2013-07-01 09:58:31 +0000
3+++ configure.ac 2013-07-09 13:37:28 +0000
4@@ -1,5 +1,5 @@
5 # When releasing also remember to update the soname as instructed below
6-AC_INIT(libunity, 7.0.6)
7+AC_INIT(libunity, 7.0.8)
8
9 AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
10 AM_CONFIG_HEADER(config.h)
11@@ -21,7 +21,10 @@
12 LIBUNITY_LT_REV=2
13 LIBUNITY_LT_AGE=0
14 LIBUNITY_LT_VERSION="$LIBUNITY_LT_CURRENT:$LIBUNITY_LT_REV:$LIBUNITY_LT_AGE"
15-LIBUNITY_LT_LDFLAGS="-version-info $LIBUNITY_LT_VERSION -export-symbols-regex '^unity_.*'"
16+# There's some autoconf weirdness which causes loss of [] characters,
17+# therefore they're doubled, the regex is to exclude unity_internal_.*
18+# symbols, it's so strange cause there's no lookahead support.
19+LIBUNITY_LT_LDFLAGS="-version-info $LIBUNITY_LT_VERSION -export-symbols-regex '^unity_([[^i]]|i[[^n]]|in[[^t]]|int[[^e]]).*'"
20
21 AC_SUBST(LIBUNITY_LT_CURRENT)
22 AC_SUBST(LIBUNITY_LT_VERSION)
23
24=== modified file 'debian/changelog'
25--- debian/changelog 2013-07-03 12:54:16 +0000
26+++ debian/changelog 2013-07-09 13:37:28 +0000
27@@ -1,3 +1,9 @@
28+libunity (7.0.8-0ubuntu1) UNRELEASED; urgency=low
29+
30+ * Add support for diff models.
31+
32+ -- Michal Hruby <michal.hruby@canonical.com> Thu, 04 Jul 2013 17:40:34 +0100
33+
34 libunity (7.0.7+13.10.20130703.2-0ubuntu1) saucy; urgency=low
35
36 [ Didier Roche ]
37
38=== modified file 'debian/libunity9.symbols'
39--- debian/libunity9.symbols 2013-07-03 04:02:22 +0000
40+++ debian/libunity9.symbols 2013-07-09 13:37:28 +0000
41@@ -189,15 +189,6 @@
42 unity_check_option_filter_get_type@Base 4.0.0
43 unity_check_option_filter_new@Base 4.0.0
44 unity_collect_launcher_entry_properties@Base 3.4.6
45- unity_dee_result_set_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
46- unity_dee_result_set_construct_with_model@Base 7.0.0daily13.05.31ubuntu.unity.next
47- unity_dee_result_set_get_flush_model@Base 7.0.0daily13.05.31ubuntu.unity.next
48- unity_dee_result_set_get_results_model@Base 7.0.0daily13.05.31ubuntu.unity.next
49- unity_dee_result_set_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
50- unity_dee_result_set_new@Base 7.0.0daily13.05.31ubuntu.unity.next
51- unity_dee_result_set_new_with_model@Base 7.0.0daily13.05.31ubuntu.unity.next
52- unity_dee_result_set_set_flush_model@Base 7.0.0daily13.05.31ubuntu.unity.next
53- unity_dee_result_set_set_results_model@Base 7.0.0daily13.05.31ubuntu.unity.next
54 unity_deprecated_scope_activate_result@Base 7.0.0daily13.05.31ubuntu.unity.next
55 unity_deprecated_scope_activate_result_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
56 unity_deprecated_scope_base_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
57@@ -299,147 +290,6 @@
58 unity_inspector_get_type@Base 3.4.6
59 unity_inspector_get_unity_bus_name@Base 3.4.6
60 unity_inspector_get_unity_running@Base 3.4.6
61- unity_internal_aggregator_scope_impl_add_constraint@Base 7.0.0daily13.05.31ubuntu.unity.next
62- unity_internal_aggregator_scope_impl_add_sorter@Base 7.0.0daily13.05.31ubuntu.unity.next
63- unity_internal_aggregator_scope_impl_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
64- unity_internal_aggregator_scope_impl_get_merge_strategy@Base 7.0.0daily13.05.31ubuntu.unity.next
65- unity_internal_aggregator_scope_impl_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
66- unity_internal_aggregator_scope_impl_get_view_type@Base 7.0.0daily13.05.31ubuntu.unity.next
67- unity_internal_aggregator_scope_impl_invalidate_search@Base 7.0.0daily13.05.31ubuntu.unity.next
68- unity_internal_aggregator_scope_impl_new@Base 7.0.0daily13.05.31ubuntu.unity.next
69- unity_internal_aggregator_scope_impl_push_filter_settings@Base 7.0.0daily13.05.31ubuntu.unity.next
70- unity_internal_aggregator_scope_impl_push_results_to_scope@Base 7.0.0daily13.05.31ubuntu.unity.next
71- unity_internal_aggregator_scope_impl_push_results_to_scope_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
72- unity_internal_aggregator_scope_impl_search_scope@Base 7.0.0daily13.05.31ubuntu.unity.next
73- unity_internal_aggregator_scope_impl_search_scope_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
74- unity_internal_aggregator_scope_impl_set_active_sources@Base 7.0.0daily13.05.31ubuntu.unity.next
75- unity_internal_aggregator_scope_impl_set_active_sources_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
76- unity_internal_aggregator_scope_impl_set_merge_strategy@Base 7.0.0daily13.05.31ubuntu.unity.next
77- unity_internal_aggregator_scope_impl_set_view_type@Base 7.0.0daily13.05.31ubuntu.unity.next
78- unity_internal_aggregator_scope_impl_subscope_ids@Base 7.0.0daily13.05.31ubuntu.unity.next
79- unity_internal_category_column_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
80- unity_internal_channel_state_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
81- unity_internal_default_scope_dbus_impl_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
82- unity_internal_default_scope_dbus_impl_get_timeout@Base 7.0.0daily13.05.31ubuntu.unity.next
83- unity_internal_default_scope_dbus_impl_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
84- unity_internal_default_scope_dbus_impl_get_view_type@Base 7.0.0daily13.05.31ubuntu.unity.next
85- unity_internal_default_scope_dbus_impl_new@Base 7.0.0daily13.05.31ubuntu.unity.next
86- unity_internal_default_scope_dbus_impl_preview_internal@Base 7.0.0daily13.05.31ubuntu.unity.next
87- unity_internal_default_scope_dbus_impl_preview_internal_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
88- unity_internal_default_scope_dbus_impl_set_active_sources@Base 7.0.0daily13.05.31ubuntu.unity.next
89- unity_internal_default_scope_dbus_impl_set_active_sources_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
90- unity_internal_default_scope_dbus_impl_set_categories@Base 7.0.0daily13.05.31ubuntu.unity.next
91- unity_internal_default_scope_dbus_impl_set_filters@Base 7.0.0daily13.05.31ubuntu.unity.next
92- unity_internal_default_scope_dbus_impl_set_timeout@Base 7.0.0daily13.05.31ubuntu.unity.next
93- unity_internal_default_scope_dbus_impl_set_view_type@Base 7.0.0daily13.05.31ubuntu.unity.next
94- unity_internal_deprecated_scope_dbus_impl_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
95- unity_internal_deprecated_scope_dbus_impl_set_categories@Base 7.0.0daily13.05.31ubuntu.unity.next
96- unity_internal_deprecated_scope_dbus_impl_set_filters@Base 7.0.0daily13.05.31ubuntu.unity.next
97- unity_internal_deprecated_scope_impl_activate_action@Base 7.0.0daily13.05.31ubuntu.unity.next
98- unity_internal_deprecated_scope_impl_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
99- unity_internal_deprecated_scope_impl_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
100- unity_internal_deprecated_scope_impl_get_view_type@Base 7.0.0daily13.05.31ubuntu.unity.next
101- unity_internal_deprecated_scope_impl_new@Base 7.0.0daily13.05.31ubuntu.unity.next
102- unity_internal_deprecated_scope_impl_set_active_sources@Base 7.0.0daily13.05.31ubuntu.unity.next
103- unity_internal_deprecated_scope_impl_set_active_sources_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
104- unity_internal_deprecated_scope_impl_set_view_type@Base 7.0.0daily13.05.31ubuntu.unity.next
105- unity_internal_filter_column_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
106- unity_internal_glib_cancellable_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
107- unity_internal_glib_cancellable_get_inner@Base 7.0.0daily13.05.31ubuntu.unity.next
108- unity_internal_glib_cancellable_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
109- unity_internal_glib_cancellable_new@Base 7.0.0daily13.05.31ubuntu.unity.next
110- unity_internal_io_get_system_data_dirs@Base 7.0.0daily13.05.31ubuntu.unity.next
111- unity_internal_io_open_from_data_dirs@Base 7.0.0daily13.05.31ubuntu.unity.next
112- unity_internal_io_open_from_data_dirs_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
113- unity_internal_io_open_from_dirs@Base 7.0.0daily13.05.31ubuntu.unity.next
114- unity_internal_io_open_from_dirs_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
115- unity_internal_io_read_stream_async@Base 7.0.0daily13.05.31ubuntu.unity.next
116- unity_internal_io_read_stream_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
117- unity_internal_io_system_data_dirs@Base 7.0.0daily13.05.31ubuntu.unity.next
118- unity_internal_io_system_data_dirs_length1@Base 7.0.0daily13.05.31ubuntu.unity.next
119- unity_internal_merge_strategy_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
120- unity_internal_merge_strategy_merge_result@Base 7.0.0daily13.05.31ubuntu.unity.next
121- unity_internal_owned_name_dup@Base 7.0.4daily13.06.19
122- unity_internal_owned_name_free@Base 7.0.4daily13.06.19
123- unity_internal_owned_name_get_type@Base 7.0.4daily13.06.19
124- unity_internal_result_column_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
125- unity_internal_results_synchronizer_add_provider@Base 7.0.0daily13.05.31ubuntu.unity.next
126- unity_internal_results_synchronizer_clear@Base 7.0.0daily13.05.31ubuntu.unity.next
127- unity_internal_results_synchronizer_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
128- unity_internal_results_synchronizer_copy_model@Base 7.0.0daily13.05.31ubuntu.unity.next
129- unity_internal_results_synchronizer_disable_all_providers@Base 7.0.0daily13.05.31ubuntu.unity.next
130- unity_internal_results_synchronizer_disable_provider@Base 7.0.0daily13.05.31ubuntu.unity.next
131- unity_internal_results_synchronizer_enable_provider@Base 7.0.0daily13.05.31ubuntu.unity.next
132- unity_internal_results_synchronizer_get_merge_strategy@Base 7.0.0daily13.05.31ubuntu.unity.next
133- unity_internal_results_synchronizer_get_receiver@Base 7.0.0daily13.05.31ubuntu.unity.next
134- unity_internal_results_synchronizer_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
135- unity_internal_results_synchronizer_new@Base 7.0.0daily13.05.31ubuntu.unity.next
136- unity_internal_results_synchronizer_prepare_row_buf@Base 7.0.0daily13.05.31ubuntu.unity.next
137- unity_internal_results_synchronizer_remove_provider@Base 7.0.0daily13.05.31ubuntu.unity.next
138- unity_internal_results_synchronizer_set_merge_strategy@Base 7.0.0daily13.05.31ubuntu.unity.next
139- unity_internal_scope_channel_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
140- unity_internal_scope_channel_create_channel@Base 7.0.0daily13.05.31ubuntu.unity.next
141- unity_internal_scope_channel_get_filter_by_id@Base 7.0.0daily13.05.31ubuntu.unity.next
142- unity_internal_scope_channel_get_pushed_models@Base 7.0.0daily13.05.31ubuntu.unity.next
143- unity_internal_scope_channel_get_search_type@Base 7.0.0daily13.05.31ubuntu.unity.next
144- unity_internal_scope_channel_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
145- unity_internal_scope_channel_is_search_running@Base 7.0.0daily13.05.31ubuntu.unity.next
146- unity_internal_scope_channel_new@Base 7.0.0daily13.05.31ubuntu.unity.next
147- unity_internal_scope_channel_register_pushed_model@Base 7.0.0daily13.05.31ubuntu.unity.next
148- unity_internal_scope_channel_set_filter_base@Base 7.0.0daily13.05.31ubuntu.unity.next
149- unity_internal_scope_channel_new@Base 7.0.0daily13.05.31ubuntu.unity.next
150- unity_internal_scope_channel_set_state@Base 7.0.0daily13.05.31ubuntu.unity.next
151- unity_internal_scope_channel_wait_for_search@Base 7.0.0daily13.05.31ubuntu.unity.next
152- unity_internal_scope_channel_wait_for_search_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
153- unity_internal_scope_dbus_impl_export@Base 7.0.0daily13.05.31ubuntu.unity.next
154- unity_internal_scope_dbus_impl_get_categories_model@Base 7.0.0daily13.05.31ubuntu.unity.next
155- unity_internal_scope_dbus_impl_get_filters_model@Base 7.0.0daily13.05.31ubuntu.unity.next
156- unity_internal_scope_dbus_impl_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
157- unity_internal_scope_dbus_impl_queue_property_notification@Base 7.0.0daily13.05.31ubuntu.unity.next
158- unity_internal_scope_dbus_impl_queue_search_for_type@Base 7.0.0daily13.05.31ubuntu.unity.next
159- unity_internal_scope_dbus_impl_set_categories_model@Base 7.0.0daily13.05.31ubuntu.unity.next
160- unity_internal_scope_dbus_impl_set_filters_model@Base 7.0.0daily13.05.31ubuntu.unity.next
161- unity_internal_scope_dbus_impl_unexport@Base 7.0.0daily13.05.31ubuntu.unity.next
162- unity_internal_scope_dbus_name_manager_acquire_names@Base 7.0.4daily13.06.19
163- unity_internal_scope_dbus_name_manager_acquire_names_finish@Base 7.0.4daily13.06.19
164- unity_internal_scope_dbus_name_manager_get_default@Base 7.0.4daily13.06.19
165- unity_internal_scope_dbus_name_manager_get_type@Base 7.0.4daily13.06.19
166- unity_internal_scope_dbus_name_manager_own_name@Base 7.0.4daily13.06.19
167- unity_internal_scope_dbus_name_manager_unown_name@Base 7.0.4daily13.06.19
168- unity_internal_utils_async_mutex_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
169- unity_internal_utils_async_mutex_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
170- unity_internal_utils_async_mutex_lock@Base 7.0.0daily13.05.31ubuntu.unity.next
171- unity_internal_utils_async_mutex_lock_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
172- unity_internal_utils_async_mutex_new@Base 7.0.0daily13.05.31ubuntu.unity.next
173- unity_internal_utils_async_mutex_ref@Base 7.0.0daily13.05.31ubuntu.unity.next
174- unity_internal_utils_async_mutex_try_lock@Base 7.0.0daily13.05.31ubuntu.unity.next
175- unity_internal_utils_async_mutex_unlock@Base 7.0.0daily13.05.31ubuntu.unity.next
176- unity_internal_utils_async_mutex_unref@Base 7.0.0daily13.05.31ubuntu.unity.next
177- unity_internal_utils_async_once_construct@Base 7.0.0daily13.05.31ubuntu.unity.next
178- unity_internal_utils_async_once_enter@Base 7.0.0daily13.05.31ubuntu.unity.next
179- unity_internal_utils_async_once_enter_finish@Base 7.0.0daily13.05.31ubuntu.unity.next
180- unity_internal_utils_async_once_get_data@Base 7.0.0daily13.05.31ubuntu.unity.next
181- unity_internal_utils_async_once_get_type@Base 7.0.0daily13.05.31ubuntu.unity.next
182- unity_internal_utils_async_once_is_initialized@Base 7.0.0daily13.05.31ubuntu.unity.next
183- unity_internal_utils_async_once_leave@Base 7.0.0daily13.05.31ubuntu.unity.next
184- unity_internal_utils_async_once_new@Base 7.0.0daily13.05.31ubuntu.unity.next
185- unity_internal_utils_async_once_ref@Base 7.0.0daily13.05.31ubuntu.unity.next
186- unity_internal_utils_async_once_reset@Base 7.0.0daily13.05.31ubuntu.unity.next
187- unity_internal_utils_async_once_unref@Base 7.0.0daily13.05.31ubuntu.unity.next
188- unity_internal_utils_delegate_wrapper_free@Base 7.0.0daily13.05.31ubuntu.unity.next
189- unity_internal_utils_delegate_wrapper_new@Base 7.0.0daily13.05.31ubuntu.unity.next
190- unity_internal_utils_hash_table_to_asv@Base 7.0.0daily13.05.31ubuntu.unity.next
191- unity_internal_utils_icon_to_string@Base 7.0.0daily13.05.31ubuntu.unity.next
192- unity_internal_utils_param_spec_async_mutex@Base 7.0.0daily13.05.31ubuntu.unity.next
193- unity_internal_utils_param_spec_async_once@Base 7.0.0daily13.05.31ubuntu.unity.next
194- unity_internal_utils_value_get_async_mutex@Base 7.0.0daily13.05.31ubuntu.unity.next
195- unity_internal_utils_value_get_async_once@Base 7.0.0daily13.05.31ubuntu.unity.next
196- unity_internal_utils_value_set_async_mutex@Base 7.0.0daily13.05.31ubuntu.unity.next
197- unity_internal_utils_value_set_async_once@Base 7.0.0daily13.05.31ubuntu.unity.next
198- unity_internal_utils_value_take_async_mutex@Base 7.0.0daily13.05.31ubuntu.unity.next
199- unity_internal_utils_value_take_async_once@Base 7.0.0daily13.05.31ubuntu.unity.next
200- unity_internal_utils_wait_for_model_synchronization@Base 7.0.4daily13.06.24
201- unity_internal_utils_wait_for_model_synchronization_finish@Base 7.0.4daily13.06.24
202 unity_launcher_entry_dbus_impl_construct@Base 3.4.6
203 unity_launcher_entry_dbus_impl_get_type@Base 3.4.6
204 unity_launcher_entry_dbus_impl_new@Base 3.4.6
205
206=== modified file 'protocol/protocol-scope-interface.vala'
207--- protocol/protocol-scope-interface.vala 2013-04-25 13:26:24 +0000
208+++ protocol/protocol-scope-interface.vala 2013-07-09 13:37:28 +0000
209@@ -66,7 +66,8 @@
210 {
211 NONE = 0,
212 PRIVATE,
213- NO_FILTERING
214+ NO_FILTERING,
215+ DIFF_CHANGES
216 }
217
218 /* The error types that can be thrown from DBus methods */
219
220=== modified file 'src/Makefile.am'
221--- src/Makefile.am 2013-07-01 07:58:03 +0000
222+++ src/Makefile.am 2013-07-09 13:37:28 +0000
223@@ -102,8 +102,9 @@
224 unity-category.vala \
225 unity-filters.vala \
226 unity-preferences-manager.vala \
227+ unity-merge-strategy.vala \
228+ unity-models.vala \
229 unity-search.vala \
230- unity-merge-strategy.vala \
231 unity-synchronizer.vala \
232 unity-previews.vala \
233 unity-result-activation.vala \
234
235=== modified file 'src/unity-aggregator-scope-private.vala'
236--- src/unity-aggregator-scope-private.vala 2013-06-25 11:19:09 +0000
237+++ src/unity-aggregator-scope-private.vala 2013-07-09 13:37:28 +0000
238@@ -1444,7 +1444,7 @@
239 response.insert (key, variant);
240 }
241
242- response[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.results_model.get_seqnum ());
243+ response[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
244 return response;
245 }
246 else
247@@ -1464,9 +1464,9 @@
248
249 var search_cancellable = Unity.Cancellable.create ();
250
251- var result_set = new DeeResultSet.with_model (channel.results_model);
252+ var result_set = new DeeResultSet.with_model (channel.backend_model);
253 result_set.ttl = -1;
254- result_set.flush_model = synchronizer.receiver as Dee.SerializableModel;
255+ result_set.flush_model = channel.transfer_model;
256 AggregatedScopeSearch? aggsearch = null;
257
258 uint timer_src_id = 0;
259@@ -1506,7 +1506,7 @@
260 search_context.cancellable = search_cancellable;
261
262 aggsearch = new AggregatedScopeSearch (owner, channel.id,
263- hints, channel.results_model);
264+ hints, channel.backend_model);
265 aggsearch.set_search_context (search_context);
266
267 ulong sig_id = aggsearch.category_order_changed.connect ((indices) =>
268@@ -1621,7 +1621,7 @@
269
270 Unity.Trace.tracepoint ("%s run end: %s", Log.METHOD, owner.id);
271
272- response[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.results_model.get_seqnum ());
273+ response[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
274 if (measure_requests)
275 {
276 int64 delta_us = search_end_time - search_start_time;
277@@ -1798,14 +1798,13 @@
278 filters_model,
279 flags | ChannelFlags.NO_FILTERING);
280
281- if (channel.results_model is Dee.SharedModel)
282+ if (channel.transfer_model != null)
283 {
284- var sm = channel.results_model as Dee.SharedModel;
285- yield Internal.Utils.wait_for_model_synchronization (sm);
286+ yield Internal.Utils.wait_for_model_synchronization (channel.transfer_model);
287 }
288
289 _channels[channel.id] = channel;
290- _scopes.register_channel (channel.id, channel.results_model, merge_strategy);
291+ _scopes.register_channel (channel.id, channel.backend_model, merge_strategy);
292
293 out_hints = new HashTable<string, Variant> (str_hash, str_equal);
294 out_hints[CHANNEL_SWARM_NAME_HINT] = new Variant.string (model_name);
295@@ -1883,16 +1882,12 @@
296 sync.add_provider (provider, source_scope_id);
297 sync.copy_model (provider);
298
299- // FIXME: do we really want to do this?
300- var sm = sync.receiver as Dee.SharedModel;
301- if (sm != null)
302- {
303- sm.flush_revision_queue ();
304- }
305-
306 var result = new HashTable<string, Variant> (str_hash, str_equal);
307- var serializable_model = sync.receiver as Dee.SerializableModel;
308- result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (serializable_model.get_seqnum ());
309+ result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
310+ if (channel.transfer_model != null)
311+ {
312+ channel.transfer_model.flush_revision_queue ();
313+ }
314
315 return result;
316 }
317
318=== modified file 'src/unity-deprecated-scope-impl.vala'
319--- src/unity-deprecated-scope-impl.vala 2013-06-25 11:19:09 +0000
320+++ src/unity-deprecated-scope-impl.vala 2013-07-09 13:37:28 +0000
321@@ -394,8 +394,6 @@
322 this.results_invalidated (channel_type);
323 }
324
325- public signal void channel_search_finished (string channel_id);
326-
327 private async HashTable<string, Variant> search_internal (
328 string search_string, HashTable<string, Variant> hints,
329 ScopeChannel channel) throws ScopeError
330@@ -426,8 +424,9 @@
331 search_context.search_type = channel.get_search_type ();
332 search_context.filter_state = channel.filters;
333 search_context.search_metadata = SearchMetadata.create (hints);
334- search_context.result_set = new DeeResultSet.with_model (channel.backend_model);
335- search_context.result_set.ttl = -1;
336+ var result_set = new DeeResultSet.with_model (channel.backend_model);
337+ result_set.ttl = -1;
338+ search_context.result_set = result_set;
339 search_context.cancellable = cancellable;
340
341 // prepare new ScopeSearch instance
342@@ -457,7 +456,7 @@
343 if (SEARCH_NO_RESULTS_HINT in last_hints)
344 result[SEARCH_NO_RESULTS_HINT] = last_hints[SEARCH_NO_RESULTS_HINT];
345 }
346- result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.results_model.get_seqnum ());
347+ result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
348 return result;
349 }
350 else
351@@ -476,7 +475,7 @@
352 if (!channel.model_lock.try_lock ()) yield channel.model_lock.lock ();
353 channel.set_state (ChannelState.SEARCH_ACTIVE);
354 // don't clear the model, the deprecated scopes do that themselves
355- //channel.results_model.clear ();
356+ //channel.backend_model.clear ();
357
358 // wait for idle, so requests that came after this one can cancel this
359 // before we even run it
360@@ -489,6 +488,8 @@
361 normalized_query);
362 }
363
364+ result_set.flush_model = channel.transfer_model;
365+
366 int64 search_start_time = 0;
367 if (measure_requests) search_start_time = get_monotonic_time ();
368 int64 search_end_time = search_start_time;
369@@ -502,10 +503,7 @@
370 });
371 yield;
372
373- // resume any suspended searches
374- channel_search_finished (channel.id);
375-
376- result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.results_model.get_seqnum ());
377+ result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
378
379 Unity.Trace.tracepoint ("%s run end: %s", Log.METHOD, owner.id);
380 if (measure_requests)
381@@ -515,6 +513,11 @@
382 result[SEARCH_TIME_HINT] = new Variant.double (delta);
383 }
384
385+ if (!cancellable.is_cancelled ())
386+ {
387+ result_set.flush ();
388+ }
389+
390 // handle hints
391 var reply_hints = new_search.get_reply_hints ();
392 if (reply_hints != null)
393@@ -630,13 +633,13 @@
394 optional_schema,
395 filters_model,
396 flags);
397- var sm = channel.results_model as Dee.SharedModel;
398- if (sm != null)
399+
400+ if (channel.transfer_model != null)
401 {
402 // force AUTOMATIC flushing for the deprecated scopes as they don't
403 // expect the ResultSet to have a separate flush() method
404- sm.flush_mode = Dee.SharedModelFlushMode.AUTOMATIC;
405- yield Internal.Utils.wait_for_model_synchronization (sm);
406+ channel.transfer_model.flush_mode = Dee.SharedModelFlushMode.AUTOMATIC;
407+ yield Internal.Utils.wait_for_model_synchronization (channel.transfer_model);
408 }
409
410 _channels[channel.id] = channel;
411
412=== added file 'src/unity-models.vala'
413--- src/unity-models.vala 1970-01-01 00:00:00 +0000
414+++ src/unity-models.vala 2013-07-09 13:37:28 +0000
415@@ -0,0 +1,200 @@
416+/*
417+ * Copyright (C) 2013 Canonical, Ltd.
418+ *
419+ * This library is free software; you can redistribute it and/or modify
420+ * it under the terms of the GNU Lesser General Public License
421+ * version 3.0 as published by the Free Software Foundation.
422+ *
423+ * This library is distributed in the hope that it will be useful,
424+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
425+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
426+ * GNU Lesser General Public License version 3.0 for more details.
427+ *
428+ * You should have received a copy of the GNU Lesser General Public
429+ * License along with this library. If not, see
430+ * <http://www.gnu.org/licenses/>.
431+ *
432+ * Authored by Michal Hruby <michal.hruby@canonical.com>
433+ *
434+ */
435+
436+using GLib;
437+using Dee;
438+
439+namespace Unity.Internal {
440+
441+internal class DeeResultSet: ResultSet
442+{
443+ public DeeResultSet ()
444+ {
445+ Object ();
446+ }
447+
448+ public DeeResultSet.with_model (Dee.SerializableModel model)
449+ {
450+ Object (results_model: model);
451+ }
452+
453+ public override void constructed ()
454+ {
455+ if (results_model == null)
456+ {
457+ results_model = new Dee.SequenceModel ();
458+ results_model.set_schema_full (Internal.RESULTS_SCHEMA);
459+ results_model.set_column_names_full (Internal.RESULTS_COLUMN_NAMES);
460+ }
461+ }
462+
463+ public Dee.SerializableModel results_model { get; construct set; }
464+
465+ public override void add_result (ScopeResult result)
466+ {
467+ // ensure this matches the schema!
468+ Variant metadata_v;
469+ if (result.metadata != null)
470+ {
471+ metadata_v = result.metadata;
472+ }
473+ else
474+ {
475+ metadata_v = new Variant.array (VariantType.VARDICT.element (), {});
476+ }
477+ results_model.append (result.uri, result.icon_hint, result.category,
478+ result.result_type, result.mimetype, result.title,
479+ result.comment, result.dnd_uri, metadata_v);
480+ }
481+
482+ public override void add_result_from_variant (Variant variant)
483+ {
484+ if (variant.get_type_string () != "(ssuussssa{sv})")
485+ {
486+ warning ("Incorrect signature for %s", Log.METHOD);
487+ return;
488+ }
489+
490+ Variant row_buf[9];
491+ variant.get ("(@s@s@u@u@s@s@s@s@a{sv})",
492+ out row_buf[0], out row_buf[1],
493+ out row_buf[2], out row_buf[3],
494+ out row_buf[4], out row_buf[5],
495+ out row_buf[6], out row_buf[7],
496+ out row_buf[8]);
497+ results_model.append_row (row_buf);
498+ }
499+
500+ public Dee.SerializableModel flush_model { get; set; }
501+
502+ public override void flush ()
503+ {
504+ var diff_model = flush_model as DiffModel;
505+ if (diff_model != null)
506+ {
507+ diff_model.commit_changes ();
508+ }
509+ var sm = flush_model as Dee.SharedModel;
510+ if (sm != null)
511+ {
512+ sm.flush_revision_queue ();
513+ }
514+ }
515+}
516+
517+internal class DiffModel: Dee.SharedModel
518+{
519+ public DiffModel (Dee.Peer peer, Dee.Model target)
520+ {
521+ var model = new Dee.SequenceModel ();
522+ Object (peer: peer, back_end: model, target_model: target);
523+ }
524+
525+ public Dee.Model target_model { get; construct set; }
526+
527+ public void commit_changes ()
528+ {
529+ uint this_rows = this.get_n_rows ();
530+ uint target_rows = target_model.get_n_rows ();
531+
532+ // a few short-circuits
533+ if (target_rows == 0)
534+ {
535+ clear ();
536+ return;
537+ }
538+ else if (this_rows == 0)
539+ {
540+ // diff model is empty, copy everything from target_model
541+ Variant row_buf[9];
542+ var iter = target_model.get_first_iter ();
543+ var end_iter = target_model.get_last_iter ();
544+ while (iter != end_iter)
545+ {
546+ // vala doesn't know we're changing the array, so need to clear it
547+ for (int i = 0; i < row_buf.length; i++) row_buf[i] = null;
548+ target_model.get_row_static (iter, row_buf);
549+ this.append_row (row_buf);
550+ iter = target_model.next (iter);
551+ }
552+ return;
553+ }
554+
555+ var script = Utils.Diff.run ((int) this_rows, (int) target_rows,
556+ (index_a, index_b) =>
557+ {
558+ var this_iter = this.get_iter_at_row (index_a);
559+ var target_iter = target_model.get_iter_at_row (index_b);
560+
561+ // check categories first
562+ if (get_uint32 (this_iter, ResultColumn.CATEGORY) !=
563+ target_model.get_uint32 (target_iter, ResultColumn.CATEGORY))
564+ {
565+ return false;
566+ }
567+
568+ // consider uris + metadata primary key
569+ if (this.get_string (this_iter, ResultColumn.URI) !=
570+ target_model.get_string (target_iter, ResultColumn.URI))
571+ {
572+ return false;
573+ }
574+
575+ Variant om = target_model.get_value (target_iter, ResultColumn.METADATA);
576+ return this.get_value (this_iter, ResultColumn.METADATA).equal (om);
577+ });
578+
579+ // the script is reversed, so no need to worry about decrementing indices
580+ // after a deletion
581+ foreach (unowned Utils.Diff.Change? change in script)
582+ {
583+ int to_delete = change.deleted;
584+ var iter = this.get_iter_at_row (change.x_offset);
585+ while (to_delete > 0)
586+ {
587+ var rm_iter = iter;
588+ iter = this.next (iter);
589+ this.remove (rm_iter);
590+ to_delete--;
591+ }
592+ if (change.inserted > 0)
593+ {
594+ Variant row_buf[9];
595+ int to_insert = change.inserted;
596+ int inserted = 0;
597+ var target_iter = target_model.get_iter_at_row (change.y_offset);
598+ while (inserted < to_insert)
599+ {
600+ for (int i = 0; i < row_buf.length; i++) row_buf[i] = null;
601+ target_model.get_row_static (target_iter, row_buf);
602+ this.insert_row (change.x_offset + inserted, row_buf);
603+ // advance positions
604+ target_iter = target_model.next (target_iter);
605+ inserted++;
606+ }
607+ }
608+ }
609+
610+ assert (get_n_rows () == target_model.get_n_rows ());
611+ }
612+}
613+
614+} /* namespace Unity.Internal */
615+
616
617=== modified file 'src/unity-scope-channel.vala'
618--- src/unity-scope-channel.vala 2013-05-01 20:56:04 +0000
619+++ src/unity-scope-channel.vala 2013-07-09 13:37:28 +0000
620@@ -37,9 +37,9 @@
621 private const uint METADATA_COLUMN = 8;
622
623 public Utils.AsyncMutex model_lock;
624- /* results_model is usually a Dee.SharedModel */
625- public Dee.SerializableModel results_model;
626- /* backing model for the results_model (ie SequenceModel or FilterModel) */
627+ /* transfer_model must be a Dee.SharedModel */
628+ public Dee.SharedModel? transfer_model;
629+ /* backing model for the transfer_model (ie SequenceModel or FilterModel) */
630 public Dee.SerializableModel backend_model;
631
632 public FilterSet filters;
633@@ -138,15 +138,41 @@
634 Dee.Peer peer = ChannelFlags.PRIVATE in flags ?
635 new Dee.Server (swarm_name) : new Dee.Peer (swarm_name);
636
637+ /* If NO_FILTERING is not specified, create_backend_model () will return
638+ * a FilterModel which will ensure that all required fields
639+ * from the schema are present, otherwise the row will be ignored.
640+ *
641+ * backend_model will then point to a simple SequenceModel which is used
642+ * as a backend for the FilterModel.
643+ */
644 Dee.Model backend = create_backend_model (
645 metadata_schema, ChannelFlags.NO_FILTERING in flags,
646 out backend_model);
647
648- var sm = Object.new (typeof (Dee.SharedModel),
649- "peer", peer,
650- "back-end", backend) as Dee.SharedModel;
651- sm.flush_mode = Dee.SharedModelFlushMode.MANUAL;
652- results_model = sm;
653+ /* Careful about using DiffModel, the ResultsSynchronizer doesn't play
654+ * nice with it, as the synchronized model gets cleared and listens
655+ * only to additions, if the provider model for the synchronizer only
656+ * removes a couple of results, and leaves the rest there,
657+ * the synchronizer will think that there are no results (cause there
658+ * were no additions).
659+ * Therefore AggregatorScopes can't use DiffModels. */
660+ if (ChannelFlags.DIFF_CHANGES in flags)
661+ {
662+ var sm = new DiffModel (peer, backend);
663+ sm.flush_mode = Dee.SharedModelFlushMode.MANUAL;
664+ sm.set_schema_full (RESULTS_SCHEMA);
665+ sm.set_column_names_full (RESULTS_COLUMN_NAMES);
666+ sm.register_vardict_schema (METADATA_COLUMN, vardict_schema);
667+ transfer_model = sm;
668+ }
669+ else
670+ {
671+ var sm = Object.new (typeof (Dee.SharedModel),
672+ "peer", peer,
673+ "back-end", backend) as Dee.SharedModel;
674+ sm.flush_mode = Dee.SharedModelFlushMode.MANUAL;
675+ transfer_model = sm;
676+ }
677
678 backend_model.set_schema_full (RESULTS_SCHEMA);
679 backend_model.set_column_names_full (RESULTS_COLUMN_NAMES);
680@@ -211,6 +237,20 @@
681 return state == ChannelState.SEARCH_ACTIVE;
682 }
683
684+ public uint64 get_last_seqnum ()
685+ {
686+ if (transfer_model != null)
687+ {
688+ if (transfer_model is DiffModel)
689+ {
690+ (transfer_model as DiffModel).commit_changes ();
691+ }
692+ return transfer_model.get_seqnum ();
693+ }
694+
695+ return backend_model.get_seqnum ();
696+ }
697+
698 public void register_pushed_model (string search_string,
699 Dee.SerializableModel model)
700 {
701
702=== modified file 'src/unity-scope-dbus-impl.vala'
703--- src/unity-scope-dbus-impl.vala 2013-06-25 11:19:09 +0000
704+++ src/unity-scope-dbus-impl.vala 2013-07-09 13:37:28 +0000
705@@ -424,8 +424,6 @@
706 }
707 }
708
709- public signal void channel_search_finished (string channel_id);
710-
711 private async HashTable<string, Variant> search_internal (
712 string search_string, HashTable<string, Variant> hints,
713 ScopeChannel channel) throws ScopeError
714@@ -465,7 +463,7 @@
715 // wait for the previous search to finish and then return
716 yield channel.wait_for_search ();
717 }
718- result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.results_model.get_seqnum ());
719+ result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
720 return result;
721 }
722 else
723@@ -506,7 +504,7 @@
724 {
725 if (!channel.model_lock.try_lock ()) yield channel.model_lock.lock ();
726 channel.set_state (ChannelState.SEARCH_ACTIVE);
727- channel.results_model.clear ();
728+ channel.backend_model.clear ();
729
730 // wait for idle, so requests that came after this one can cancel this
731 // before we even run it
732@@ -519,7 +517,7 @@
733 normalized_query);
734 }
735
736- result_set.flush_model = channel.results_model;
737+ result_set.flush_model = channel.transfer_model;
738
739 int64 search_start_time = 0;
740 if (measure_requests) search_start_time = get_monotonic_time ();
741@@ -549,12 +547,9 @@
742 yield;
743 }
744
745- // resume any suspended searches
746- channel_search_finished (channel.id);
747-
748 Unity.Trace.tracepoint ("%s run end: %s", Log.METHOD, _dbus_name);
749
750- result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.results_model.get_seqnum ());
751+ result[SEARCH_SEQNUM_HINT] = new Variant.uint64 (channel.get_last_seqnum ());
752 if (measure_requests)
753 {
754 int64 delta_us = search_end_time - search_start_time;
755@@ -699,10 +694,9 @@
756 filters_model,
757 flags);
758
759- if (channel.results_model is Dee.SharedModel)
760+ if (channel.transfer_model != null)
761 {
762- var sm = channel.results_model as Dee.SharedModel;
763- yield Internal.Utils.wait_for_model_synchronization (sm);
764+ yield Internal.Utils.wait_for_model_synchronization (channel.transfer_model);
765 }
766
767 _channels[channel.id] = channel;
768
769=== modified file 'src/unity-scope-interface.vala'
770--- src/unity-scope-interface.vala 2013-06-25 09:00:37 +0000
771+++ src/unity-scope-interface.vala 2013-07-09 13:37:28 +0000
772@@ -182,7 +182,7 @@
773 * Abstract method where the search is performed
774 *
775 * Scopes need to implement this method and add results to the ResultSet
776- * which was passed to the Unity.AbstractScope.create_search_for_query().
777+ * which was passed to the {@link Unity.AbstractScope.create_search_for_query}.
778 * By the time this method returns, the ResultSet is considered complete.
779 */
780 public abstract void run ();
781
782=== modified file 'src/unity-search.vala'
783--- src/unity-search.vala 2013-06-18 14:17:57 +0000
784+++ src/unity-search.vala 2013-07-09 13:37:28 +0000
785@@ -37,77 +37,6 @@
786
787 } /* namespace Unity.Internal */
788
789-public class DeeResultSet: ResultSet
790-{
791- public class DeeResultSet ()
792- {
793- Object ();
794- }
795-
796- public class DeeResultSet.with_model (Dee.SerializableModel model)
797- {
798- Object (results_model: model);
799- }
800-
801- public override void constructed ()
802- {
803- if (results_model == null)
804- {
805- results_model = new Dee.SequenceModel ();
806- results_model.set_schema_full (Internal.RESULTS_SCHEMA);
807- results_model.set_column_names_full (Internal.RESULTS_COLUMN_NAMES);
808- }
809- }
810-
811- public Dee.SerializableModel results_model { get; construct set; }
812-
813- public override void add_result (ScopeResult result)
814- {
815- // ensure this matches the schema!
816- Variant metadata_v;
817- if (result.metadata != null)
818- {
819- metadata_v = result.metadata;
820- }
821- else
822- {
823- metadata_v = new Variant.array (VariantType.VARDICT.element (), {});
824- }
825- results_model.append (result.uri, result.icon_hint, result.category,
826- result.result_type, result.mimetype, result.title,
827- result.comment, result.dnd_uri, metadata_v);
828- }
829-
830- public override void add_result_from_variant (Variant variant)
831- {
832- if (variant.get_type_string () != "(ssuussssa{sv})")
833- {
834- warning ("Incorrect signature for %s", Log.METHOD);
835- return;
836- }
837-
838- Variant row_buf[9];
839- variant.get ("(@s@s@u@u@s@s@s@s@a{sv})",
840- out row_buf[0], out row_buf[1],
841- out row_buf[2], out row_buf[3],
842- out row_buf[4], out row_buf[5],
843- out row_buf[6], out row_buf[7],
844- out row_buf[8]);
845- results_model.append_row (row_buf);
846- }
847-
848- public Dee.SerializableModel flush_model { get; set; }
849-
850- public override void flush ()
851- {
852- var sm = flush_model as Dee.SharedModel;
853- if (sm != null)
854- {
855- sm.flush_revision_queue ();
856- }
857- }
858-}
859-
860 /**
861 * Internal transitioning class, note that it will disappear from the public
862 * API as soon as possible and therefore shouldn't be used.
863
864=== modified file 'src/unity-utils.vala'
865--- src/unity-utils.vala 2013-06-24 16:05:00 +0000
866+++ src/unity-utils.vala 2013-07-09 13:37:28 +0000
867@@ -305,6 +305,347 @@
868 {
869 return icon != null ? icon.to_string () : "";
870 }
871+
872+ namespace Diff
873+ {
874+ internal delegate bool ResultSetCompareFunc (int index_a, int index_b);
875+
876+ private struct Context
877+ {
878+ // Lengths of the analyzed sets
879+ int x_length;
880+ int y_length;
881+ // Buffer for finding the minimal edit path, contains space for both
882+ // forwards and backwards paths
883+ int[] real_diag;
884+ // Buffer for finding forwards edit path, indexed from (-y_length-1)
885+ // to (x_length+1)
886+ int* f_diag;
887+ // Buffer for finding backwards edit path, indexed from (-y_length-1)
888+ // to (x_length+1)
889+ int* b_diag;
890+ // Maximum cost, finding the optimal one is often too costly
891+ int max_cost;
892+ // Buffer combining the x and y change buffers
893+ uint8[] real_changes;
894+ // Buffer for recording which items in the original set were removed
895+ uint8* x_changes;
896+ // Buffer for recording which items in the target set were added
897+ uint8* y_changes;
898+
899+ Context (int x_set_length, int y_set_length)
900+ {
901+ x_length = x_set_length;
902+ y_length = y_set_length;
903+ int diags = (x_set_length + 1 + y_set_length + 1) + 1;
904+
905+ // allocate one big buffer for the forward and backward vectors
906+ real_diag = new int[diags * 2];
907+ // offset the pointers, cause we're sharing the same mem block,
908+ // plus the indices can be negative
909+ f_diag = real_diag;
910+ f_diag += y_set_length + 1;
911+ b_diag = real_diag;
912+ b_diag += diags;
913+ b_diag += y_set_length + 1;
914+
915+ // another one big buffer to record the changes
916+ real_changes = new uint8[diags];
917+ x_changes = real_changes;
918+ y_changes = real_changes;
919+ y_changes += x_set_length + 1;
920+
921+ // finding the optimal solution could be too expensive, use suboptimal
922+ // if the cost is getting too high
923+ max_cost = 1;
924+ // fast sqrt(diags) approximation
925+ while (diags != 0)
926+ {
927+ diags /= 4;
928+ max_cost *= 2;
929+ }
930+ max_cost = int.max (max_cost, 256);
931+ }
932+ }
933+
934+ private struct Partition
935+ {
936+ // find the midpoints where we need to split the two sets
937+ int x_mid;
938+ int y_mid;
939+ // is the upper and lower part of the edit path optimal?
940+ bool lo_minimal;
941+ bool hi_minimal;
942+ }
943+
944+ internal struct Change
945+ {
946+ int x_offset;
947+ int y_offset;
948+ int inserted;
949+ int deleted;
950+ }
951+
952+ internal static SList<Change?> run (int x_set_length, int y_set_length,
953+ ResultSetCompareFunc cmp_func)
954+ {
955+ Context ctx = Context (x_set_length, y_set_length);
956+
957+ compare_sequences (0, x_set_length, 0, y_set_length,
958+ false, ref ctx, cmp_func);
959+
960+ return build_edit_script (ref ctx);
961+ }
962+
963+ // Note that this produces changeset in reversed order
964+ private static SList<Change?> build_edit_script (ref Context ctx)
965+ {
966+ SList<Change?> script = new SList<Change?> ();
967+ int x_length = ctx.x_length;
968+ int y_length = ctx.y_length;
969+ uint8* x_changes = ctx.x_changes;
970+ uint8* y_changes = ctx.y_changes;
971+ int x = 0;
972+ int y = 0;
973+ // find continous change sets and record them, so we're able
974+ // to transform the first set into the second
975+ while (x < x_length || y < y_length)
976+ {
977+ if ((x_changes[x] | y_changes[y]) != 0)
978+ {
979+ int xx = x;
980+ int yy = y;
981+ while (x_changes[x] != 0) x++;
982+ while (y_changes[y] != 0) y++;
983+
984+ script.prepend ({xx, yy, y - yy, x - xx});
985+ }
986+
987+ x++;
988+ y++;
989+ }
990+
991+ return script;
992+ }
993+
994+ /* Find the midpoint of the shortest edit script for a given subset
995+ of the two vectors */
996+ private static void find_diag (int x_offset, int x_limit,
997+ int y_offset, int y_limit,
998+ ResultSetCompareFunc equal_func,
999+ ref Context ctx,
1000+ ref Partition partition)
1001+ {
1002+ int d_min = x_offset - y_limit;
1003+ int d_max = x_limit - y_offset;
1004+ int f_mid = x_offset - y_offset;
1005+ int b_mid = x_limit - y_limit;
1006+
1007+ int f_min = f_mid;
1008+ int f_max = f_mid;
1009+ int b_min = b_mid;
1010+ int b_max = b_mid;
1011+
1012+ int cost;
1013+ bool is_odd = ((f_mid - b_mid) & 1) != 0;
1014+
1015+ int* f_diag = ctx.f_diag;
1016+ int* b_diag = ctx.b_diag;
1017+ f_diag[f_mid] = x_offset;
1018+ b_diag[b_mid] = x_limit;
1019+
1020+ for (cost = 1; ; cost++)
1021+ {
1022+ int d;
1023+
1024+ // extend the forwards search by an edit step in each diagonal
1025+ if (f_min > d_min) f_diag[--f_min - 1] = -1;
1026+ else ++f_min;
1027+
1028+ if (f_max < d_max) f_diag[++f_max + 1] = -1;
1029+ else --f_max;
1030+
1031+ for (d = f_max; d >= f_min; d -= 2)
1032+ {
1033+ int x, y;
1034+ int t_lo = f_diag[d - 1];
1035+ int t_hi = f_diag[d + 1];
1036+ int x0 = t_lo < t_hi ? t_hi : t_lo + 1;
1037+
1038+ for (x = x0, y = x0 - d;
1039+ x < x_limit && y < y_limit && equal_func (x, y);
1040+ x++, y++)
1041+ {
1042+ continue;
1043+ }
1044+
1045+ f_diag[d] = x;
1046+ if (is_odd && b_min <= d && d <= b_max && b_diag[d] <= x)
1047+ {
1048+ partition.x_mid = x;
1049+ partition.y_mid = y;
1050+ partition.lo_minimal = partition.hi_minimal = true;
1051+ return;
1052+ }
1053+ }
1054+
1055+ // and extend the backwards search
1056+ if (b_min > d_min) b_diag[--b_min - 1] = int.MAX;
1057+ else ++b_min;
1058+
1059+ if (b_max < d_max) b_diag[++b_max + 1] = int.MAX;
1060+ else --b_max;
1061+
1062+ for (d = b_max; d >= b_min; d -= 2)
1063+ {
1064+ int x, y;
1065+ int t_lo = b_diag[d - 1];
1066+ int t_hi = b_diag[d + 1];
1067+ int x0 = t_lo < t_hi ? t_lo : t_hi - 1;
1068+
1069+ for (x = x0, y = x0 - d;
1070+ x_offset < x && y_offset < y && equal_func (x-1, y-1);
1071+ x--, y--)
1072+ {
1073+ continue;
1074+ }
1075+
1076+ b_diag[d] = x;
1077+ if (!is_odd && f_min <= d && d <= f_max && x <= f_diag[d])
1078+ {
1079+ partition.x_mid = x;
1080+ partition.y_mid = y;
1081+ partition.lo_minimal = partition.hi_minimal = true;
1082+ return;
1083+ }
1084+ }
1085+ // Chance to implement heuristic to speed things up at the cost
1086+ // of loosing optimal path
1087+
1088+ // If cost is too high, give up and report halfway between best results
1089+ if (cost >= ctx.max_cost)
1090+ {
1091+ int fxy_best, bxy_best;
1092+ int fx_best = 0;
1093+ int bx_best = 0;
1094+
1095+ fxy_best = -1;
1096+ for (d = f_max; d >= f_min; d -= 2)
1097+ {
1098+ int x = int.min (f_diag[d], x_limit);
1099+ int y = x - d;
1100+ if (y_limit < y)
1101+ {
1102+ x = y_limit + d;
1103+ y = y_limit;
1104+ }
1105+ if (fxy_best < x + y)
1106+ {
1107+ fxy_best = x + y;
1108+ fx_best = x;
1109+ }
1110+ }
1111+
1112+ bxy_best = int.MAX;
1113+ for (d = b_max; d >= b_min; d -= 2)
1114+ {
1115+ int x = int.max (x_offset, b_diag[d]);
1116+ int y = x - d;
1117+ if (y < y_offset)
1118+ {
1119+ x = y_offset + d;
1120+ y = y_offset;
1121+ }
1122+ if (x + y < bxy_best)
1123+ {
1124+ bxy_best = x + y;
1125+ bx_best = x;
1126+ }
1127+ }
1128+
1129+ if ((x_limit + y_limit) - bxy_best < fxy_best - (x_offset + y_offset))
1130+ {
1131+ partition.x_mid = fx_best;
1132+ partition.y_mid = fxy_best - fx_best;
1133+ partition.lo_minimal = true;
1134+ partition.hi_minimal = false;
1135+ }
1136+ else
1137+ {
1138+ partition.x_mid = bx_best;
1139+ partition.y_mid = bxy_best - bx_best;
1140+ partition.lo_minimal = false;
1141+ partition.hi_minimal = true;
1142+ }
1143+ return;
1144+ }
1145+ }
1146+ }
1147+
1148+ /* Compare contigous sequences of two sets.
1149+ *
1150+ * Return true if terminated through early abort, false otherwise.
1151+ */
1152+ private static bool compare_sequences (int x_offset, int x_limit,
1153+ int y_offset, int y_limit,
1154+ bool find_minimal, ref Context ctx,
1155+ ResultSetCompareFunc equal_func)
1156+ {
1157+ // slide down the bottom diagonal in the forwards path
1158+ while (x_offset < x_limit && y_offset < y_limit && equal_func (x_offset, y_offset))
1159+ {
1160+ x_offset++;
1161+ y_offset++;
1162+ }
1163+
1164+ // and slide up the top diagonal in the backwards path
1165+ while (x_offset < x_limit && y_offset < y_limit && equal_func (x_limit-1, y_limit-1))
1166+ {
1167+ x_limit--;
1168+ y_limit--;
1169+ }
1170+
1171+ if (x_offset == x_limit)
1172+ {
1173+ // these items were added to the target set
1174+ while (y_offset < y_limit)
1175+ {
1176+ ctx.y_changes[y_offset] = 1;
1177+ y_offset++;
1178+ }
1179+ }
1180+ else if (y_offset == y_limit)
1181+ {
1182+ // these items were removed from the original set
1183+ while (x_offset < x_limit)
1184+ {
1185+ ctx.x_changes[x_offset] = 1;
1186+ x_offset++;
1187+ }
1188+ }
1189+ else
1190+ {
1191+ Partition partition = { 0, 0, false, false };
1192+
1193+ // split into two subproblems
1194+ find_diag (x_offset, x_limit, y_offset, y_limit,
1195+ equal_func, ref ctx, ref partition);
1196+
1197+ if (compare_sequences (x_offset, partition.x_mid,
1198+ y_offset, partition.y_mid,
1199+ partition.lo_minimal,
1200+ ref ctx, equal_func))
1201+ return true;
1202+ if (compare_sequences (partition.x_mid, x_limit,
1203+ partition.y_mid, y_limit,
1204+ partition.hi_minimal,
1205+ ref ctx, equal_func))
1206+ return true;
1207+ }
1208+
1209+ return false;
1210+ }
1211+ } /* namespace Unity.Internal.Utils.Diff */
1212 }
1213
1214 } /* namespace Unity.Internal */
1215
1216=== modified file 'test/vala/Makefile.am'
1217--- test/vala/Makefile.am 2013-07-02 18:42:23 +0000
1218+++ test/vala/Makefile.am 2013-07-09 13:37:28 +0000
1219@@ -13,6 +13,7 @@
1220 -DTESTDIR=\"$(top_srcdir)/test\" \
1221 -DTESTVALADIR=\"$(top_srcdir)/test/vala\" \
1222 -DG_SETTINGS_ENABLE_BACKEND \
1223+ -ggdb \
1224 $(LIBUNITY_CFLAGS) \
1225 $(LIBUNITY_LIBS)
1226
1227@@ -56,10 +57,12 @@
1228 TEST_PROGS += test-vala test-scope test-blacklist-crash test-extras
1229
1230 test_vala_LDADD = $(test_libs)
1231+test_vala_LDFLAGS = -static
1232
1233 test_vala_VALASOURCES = \
1234 common.vala \
1235 test-appinfo-manager.vala \
1236+ test-diff.vala \
1237 test-filters.vala \
1238 test-io.vala \
1239 test-launcher.vala \
1240@@ -74,6 +77,7 @@
1241 nodist_test_vala_SOURCES = $(test_vala_VALASOURCES:.vala=.c)
1242
1243 test_scope_LDADD = $(test_libs)
1244+test_scope_LDFLAGS = -static
1245 test_scope_VALASOURCES = common.vala test-scope.vala
1246 nodist_test_scope_SOURCES = $(test_scope_VALASOURCES:.vala=.c)
1247
1248
1249=== added file 'test/vala/test-diff.vala'
1250--- test/vala/test-diff.vala 1970-01-01 00:00:00 +0000
1251+++ test/vala/test-diff.vala 2013-07-09 13:37:28 +0000
1252@@ -0,0 +1,507 @@
1253+/* -*- Mode: vala; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
1254+/*
1255+ * Copyright (C) 2011 Canonical Ltd
1256+ *
1257+ * This program is free software: you can redistribute it and/or modify
1258+ * it under the terms of the GNU General Public License version 3 as
1259+ * published by the Free Software Foundation.
1260+ *
1261+ * This program is distributed in the hope that it will be useful,
1262+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1263+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1264+ * GNU General Public License for more details.
1265+ *
1266+ * You should have received a copy of the GNU General Public License
1267+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1268+ *
1269+ * Authored by Michal Hruby <michal.hruby@canonical.com>
1270+ *
1271+ */
1272+using Unity.Internal;
1273+using Unity.Internal.Utils.Diff;
1274+
1275+namespace Unity.Test
1276+{
1277+ public class DiffSuite
1278+ {
1279+ public DiffSuite ()
1280+ {
1281+ GLib.Test.add_data_func ("/Unit/Diff/Empty",
1282+ Fixture.create<DiffTester> (DiffTester.test_empty_diff));
1283+ GLib.Test.add_data_func ("/Unit/Diff/Populate",
1284+ Fixture.create<DiffTester> (DiffTester.test_populate));
1285+ GLib.Test.add_data_func ("/Unit/Diff/Identical",
1286+ Fixture.create<DiffTester> (DiffTester.test_identical));
1287+ GLib.Test.add_data_func ("/Unit/Diff/Appends",
1288+ Fixture.create<DiffTester> (DiffTester.test_appends));
1289+ GLib.Test.add_data_func ("/Unit/Diff/Prepends",
1290+ Fixture.create<DiffTester> (DiffTester.test_prepends));
1291+ GLib.Test.add_data_func ("/Unit/Diff/Inserts",
1292+ Fixture.create<DiffTester> (DiffTester.test_inserts));
1293+ GLib.Test.add_data_func ("/Unit/Diff/MoveToMiddle",
1294+ Fixture.create<DiffTester> (DiffTester.test_move_to_middle));
1295+ GLib.Test.add_data_func ("/Unit/Diff/RemoveBegin",
1296+ Fixture.create<DiffTester> (DiffTester.test_remove_begin));
1297+ GLib.Test.add_data_func ("/Unit/Diff/RemoveEnd",
1298+ Fixture.create<DiffTester> (DiffTester.test_remove_end));
1299+ GLib.Test.add_data_func ("/Unit/Diff/RemoveMid",
1300+ Fixture.create<DiffTester> (DiffTester.test_remove_mid));
1301+ GLib.Test.add_data_func ("/Unit/Diff/Mixed",
1302+ Fixture.create<DiffTester> (DiffTester.test_mixed));
1303+
1304+ GLib.Test.add_data_func ("/Unit/DiffModel/Populate",
1305+ Fixture.create<DiffModelTester> (DiffModelTester.test_populate));
1306+ GLib.Test.add_data_func ("/Unit/DiffModel/Clear",
1307+ Fixture.create<DiffModelTester> (DiffModelTester.test_clear));
1308+ GLib.Test.add_data_func ("/Unit/DiffModel/Mixed",
1309+ Fixture.create<DiffModelTester> (DiffModelTester.test_mixed));
1310+ GLib.Test.add_data_func ("/Unit/DiffModel/ChangedMetadata",
1311+ Fixture.create<DiffModelTester> (DiffModelTester.test_metadata));
1312+ }
1313+
1314+ class DiffTester: Object, Fixture
1315+ {
1316+ private void setup ()
1317+ {
1318+ }
1319+
1320+ private void teardown ()
1321+ {
1322+ }
1323+
1324+ private SList<Change?> run_diff (string[] x, string[] y)
1325+ {
1326+ var script = run (x.length, y.length, (a, b) =>
1327+ {
1328+ return x[a] == y[b];
1329+ });
1330+ script.reverse ();
1331+ return script;
1332+ }
1333+
1334+ public void test_empty_diff ()
1335+ {
1336+ string[] x_results = {};
1337+ string[] y_results = {};
1338+
1339+ var script = run_diff (x_results, y_results);
1340+
1341+ assert (script.length () == 0);
1342+ }
1343+
1344+ public void test_populate ()
1345+ {
1346+ string[] x_results = {};
1347+ string[] y_results = {};
1348+ y_results += "Line #1";
1349+ y_results += "Line #2";
1350+ y_results += "Line #3";
1351+ y_results += "Line #4";
1352+
1353+ var script = run_diff (x_results, y_results);
1354+
1355+ assert (script.length () == 1);
1356+ var change = script.nth_data (0);
1357+ assert (change.x_offset == 0);
1358+ assert (change.y_offset == 0);
1359+ assert (change.inserted == 4);
1360+ assert (change.deleted == 0);
1361+ }
1362+
1363+ public void test_identical ()
1364+ {
1365+ string[] x_results = {};
1366+ x_results += "Line #1";
1367+ x_results += "Line #2";
1368+ x_results += "Line #3";
1369+ x_results += "Line #4";
1370+ string[] y_results = {};
1371+ y_results += "Line #1";
1372+ y_results += "Line #2";
1373+ y_results += "Line #3";
1374+ y_results += "Line #4";
1375+
1376+ var script = run_diff (x_results, y_results);
1377+
1378+ assert (script.length () == 0);
1379+ }
1380+
1381+ public void test_appends ()
1382+ {
1383+ string[] x_results = {};
1384+ x_results += "Line #1";
1385+ x_results += "Line #2";
1386+ x_results += "Line #3";
1387+ x_results += "Line #4";
1388+ string[] y_results = {};
1389+ y_results += "Line #1";
1390+ y_results += "Line #2";
1391+ y_results += "Line #3";
1392+ y_results += "Line #4";
1393+ y_results += "Line #5";
1394+ y_results += "Line #6";
1395+ y_results += "Line #7";
1396+
1397+ var script = run_diff (x_results, y_results);
1398+
1399+ assert (script.length () == 1);
1400+ var change = script.nth_data (0);
1401+ assert (change.x_offset == 4);
1402+ assert (change.y_offset == 4);
1403+ assert (change.inserted == 3);
1404+ assert (change.deleted == 0);
1405+ }
1406+
1407+ public void test_prepends ()
1408+ {
1409+ string[] x_results = {};
1410+ x_results += "Line #1";
1411+ x_results += "Line #2";
1412+ x_results += "Line #3";
1413+ x_results += "Line #4";
1414+ string[] y_results = {};
1415+ y_results += "Line #5";
1416+ y_results += "Line #6";
1417+ y_results += "Line #7";
1418+ y_results += "Line #1";
1419+ y_results += "Line #2";
1420+ y_results += "Line #3";
1421+ y_results += "Line #4";
1422+
1423+ var script = run_diff (x_results, y_results);
1424+
1425+ assert (script.length () == 1);
1426+ var change = script.nth_data (0);
1427+ assert (change.x_offset == 0);
1428+ assert (change.y_offset == 0);
1429+ assert (change.inserted == 3);
1430+ assert (change.deleted == 0);
1431+ }
1432+
1433+ public void test_inserts ()
1434+ {
1435+ string[] x_results = {};
1436+ x_results += "Line #2";
1437+ x_results += "Line #4";
1438+ string[] y_results = {};
1439+ y_results += "Line #1";
1440+ y_results += "Line #2";
1441+ y_results += "Line #3";
1442+ y_results += "Line #4";
1443+ y_results += "Line #5";
1444+
1445+ var script = run_diff (x_results, y_results);
1446+
1447+ assert (script.length () == 3);
1448+ var change = script.nth_data (0);
1449+ assert (change.x_offset == 0);
1450+ assert (change.y_offset == 0);
1451+ assert (change.inserted == 1);
1452+ assert (change.deleted == 0);
1453+ change = script.nth_data (1);
1454+ assert (change.x_offset == 1);
1455+ assert (change.y_offset == 2);
1456+ assert (change.inserted == 1);
1457+ assert (change.deleted == 0);
1458+ change = script.nth_data (2);
1459+ assert (change.x_offset == 2);
1460+ assert (change.y_offset == 4);
1461+ assert (change.inserted == 1);
1462+ assert (change.deleted == 0);
1463+ }
1464+
1465+ public void test_move_to_middle ()
1466+ {
1467+ string[] x_results = {};
1468+ x_results += "Original";
1469+ string[] y_results = {};
1470+ for (int i = 0; i < 86; i++)
1471+ {
1472+ if (i == 40) y_results += "Original";
1473+ else y_results += "Line #%d".printf (i);
1474+ }
1475+
1476+ var script = run_diff (x_results, y_results);
1477+
1478+ assert (script.length () == 2);
1479+ var change = script.nth_data (0);
1480+ assert (change.inserted == 40);
1481+ change = script.nth_data (1);
1482+ assert (change.inserted == 45);
1483+ }
1484+
1485+ public void test_remove_begin ()
1486+ {
1487+ string[] x_results = {};
1488+ x_results += "Line #1";
1489+ x_results += "Line #2";
1490+ x_results += "Line #3";
1491+ x_results += "Line #4";
1492+ string[] y_results = {};
1493+ y_results += "Line #3";
1494+ y_results += "Line #4";
1495+
1496+ var script = run_diff (x_results, y_results);
1497+
1498+ assert (script.length () == 1);
1499+ var change = script.nth_data (0);
1500+ assert (change.x_offset == 0);
1501+ assert (change.y_offset == 0);
1502+ assert (change.inserted == 0);
1503+ assert (change.deleted == 2);
1504+ }
1505+
1506+ public void test_remove_end ()
1507+ {
1508+ string[] x_results = {};
1509+ x_results += "Line #1";
1510+ x_results += "Line #2";
1511+ x_results += "Line #3";
1512+ x_results += "Line #4";
1513+ string[] y_results = {};
1514+ y_results += "Line #1";
1515+ y_results += "Line #2";
1516+
1517+ var script = run_diff (x_results, y_results);
1518+
1519+ assert (script.length () == 1);
1520+ var change = script.nth_data (0);
1521+ assert (change.x_offset == 2);
1522+ assert (change.y_offset == 2);
1523+ assert (change.inserted == 0);
1524+ assert (change.deleted == 2);
1525+ }
1526+
1527+ public void test_remove_mid ()
1528+ {
1529+ string[] x_results = {};
1530+ x_results += "Line #1";
1531+ x_results += "Line #2";
1532+ x_results += "Line #3";
1533+ x_results += "Line #4";
1534+ string[] y_results = {};
1535+ y_results += "Line #1";
1536+ y_results += "Line #4";
1537+
1538+ var script = run_diff (x_results, y_results);
1539+
1540+ assert (script.length () == 1);
1541+ var change = script.nth_data (0);
1542+ assert (change.x_offset == 1);
1543+ assert (change.y_offset == 1);
1544+ assert (change.inserted == 0);
1545+ assert (change.deleted == 2);
1546+ }
1547+
1548+ public void test_mixed ()
1549+ {
1550+ string[] x_results = {};
1551+ x_results += "Line #1";
1552+ x_results += "Line #2";
1553+ x_results += "Line #3";
1554+ x_results += "Line #4";
1555+ x_results += "Line #5";
1556+ x_results += "Line #6";
1557+ x_results += "Line #7";
1558+ x_results += "Line #8";
1559+ x_results += "Line #9";
1560+ x_results += "Line #10";
1561+ string[] y_results = {};
1562+ y_results += "Line #3";
1563+ y_results += "Line #4";
1564+ y_results += "Line #5";
1565+ y_results += "Line #6";
1566+ y_results += "Line #7";
1567+ y_results += "Added #1";
1568+ y_results += "Added #2";
1569+ y_results += "Line #8";
1570+ y_results += "Line #9";
1571+ y_results += "Line #12";
1572+ y_results += "Line #14";
1573+
1574+ var script = run_diff (x_results, y_results);
1575+
1576+ assert (script.length () == 3);
1577+ var change = script.nth_data (0);
1578+ assert (change.x_offset == 0);
1579+ assert (change.y_offset == 0);
1580+ assert (change.inserted == 0);
1581+ assert (change.deleted == 2);
1582+ change = script.nth_data (1);
1583+ assert (change.x_offset == 7);
1584+ assert (change.y_offset == 5);
1585+ assert (change.inserted == 2);
1586+ assert (change.deleted == 0);
1587+ change = script.nth_data (2);
1588+ assert (change.x_offset == 9);
1589+ assert (change.y_offset == 9);
1590+ assert (change.inserted == 2);
1591+ assert (change.deleted == 1);
1592+ }
1593+ }
1594+
1595+ class DiffModelTester: Object, Fixture
1596+ {
1597+ private Unity.Internal.DiffModel? model;
1598+ private Dee.SequenceModel? backend_model;
1599+ private uint rows_added;
1600+ private uint rows_removed;
1601+
1602+ private void setup ()
1603+ {
1604+ backend_model = new Dee.SequenceModel ();
1605+ backend_model.set_schema_full (RESULTS_SCHEMA);
1606+ backend_model.set_column_names_full (RESULTS_COLUMN_NAMES);
1607+
1608+ var peer = new Dee.Server ("com.canonical.Libunity.Test");
1609+ model = new Unity.Internal.DiffModel (peer, backend_model);
1610+ model.set_schema_full (RESULTS_SCHEMA);
1611+ model.set_column_names_full (RESULTS_COLUMN_NAMES);
1612+ model.row_added.connect (() => { rows_added++; });
1613+ model.row_removed.connect (() => { rows_removed++; });
1614+
1615+ var ml = new MainLoop ();
1616+ Utils.wait_for_model_synchronization (model, (obj, res) =>
1617+ {
1618+ ml.quit ();
1619+ });
1620+ assert (run_with_timeout (ml));
1621+ }
1622+
1623+ private void teardown ()
1624+ {
1625+ model = null;
1626+ backend_model = null;
1627+ }
1628+
1629+ private void add_sample_result (
1630+ string uri,
1631+ uint category,
1632+ HashTable<string, Variant>? metadata = null)
1633+ {
1634+ Variant metadata_v = metadata != null ?
1635+ metadata : new Variant.array (VariantType.VARDICT.element (), {});
1636+ backend_model.append (uri, "icon", category, 0, "text/plain",
1637+ Path.get_basename (uri), "", uri, metadata_v);
1638+ }
1639+
1640+ public void test_populate ()
1641+ {
1642+ assert (backend_model.get_n_rows () == 0);
1643+ assert (model.get_n_rows () == 0);
1644+ assert (model.target_model.get_n_rows () == 0);
1645+
1646+ add_sample_result ("file:///test1", 0);
1647+ add_sample_result ("file:///test2", 0);
1648+ add_sample_result ("file:///test9", 1);
1649+ add_sample_result ("file:///test2", 1);
1650+
1651+ assert (backend_model.get_n_rows () == 4);
1652+ assert (model.target_model.get_n_rows () == 4);
1653+ assert (model.get_n_rows () == 0);
1654+ assert (rows_added == 0);
1655+ assert (rows_removed == 0);
1656+
1657+ model.commit_changes ();
1658+ assert (model.get_n_rows () == 4);
1659+ assert (rows_added == 4);
1660+ assert (rows_removed == 0);
1661+ }
1662+
1663+ public void test_clear ()
1664+ {
1665+ add_sample_result ("file:///test1", 0);
1666+ add_sample_result ("file:///test2", 0);
1667+ add_sample_result ("file:///test3", 0);
1668+ add_sample_result ("file:///test9", 1);
1669+ add_sample_result ("file:///test2", 1);
1670+ add_sample_result ("file:///test5", 4);
1671+
1672+ model.commit_changes ();
1673+ assert (model.get_n_rows () == backend_model.get_n_rows ());
1674+ assert (rows_added == backend_model.get_n_rows ());
1675+ assert (rows_removed == 0);
1676+
1677+ backend_model.clear ();
1678+
1679+ rows_added = 0;
1680+ rows_removed = 0;
1681+ model.commit_changes ();
1682+ assert (model.get_n_rows () == backend_model.get_n_rows ());
1683+ assert (rows_added == 0);
1684+ assert (rows_removed == 6);
1685+ }
1686+
1687+ public void test_mixed ()
1688+ {
1689+ add_sample_result ("file:///test1", 0);
1690+ add_sample_result ("file:///test2", 0);
1691+ add_sample_result ("file:///test3", 0);
1692+ add_sample_result ("file:///test9", 1);
1693+ add_sample_result ("file:///test2", 1);
1694+ add_sample_result ("file:///test5", 4);
1695+
1696+ model.commit_changes ();
1697+ assert (model.get_n_rows () == backend_model.get_n_rows ());
1698+ assert (rows_added == backend_model.get_n_rows ());
1699+ assert (rows_removed == 0);
1700+
1701+ backend_model.clear ();
1702+ add_sample_result ("file:///test1", 0);
1703+ add_sample_result ("file:///test3", 0);
1704+ add_sample_result ("file:///test9", 1);
1705+ add_sample_result ("file:///test2", 1);
1706+ add_sample_result ("file:///test5", 4);
1707+ add_sample_result ("file:///test2", 4);
1708+ add_sample_result ("file:///test1", 4);
1709+
1710+ rows_added = 0;
1711+ rows_removed = 0;
1712+ model.commit_changes ();
1713+ assert (model.get_n_rows () == backend_model.get_n_rows ());
1714+ assert (rows_added == 2);
1715+ assert (rows_removed == 1);
1716+ // compare the actual values and their ordering
1717+ for (uint i = 0; i < model.get_n_rows (); i++)
1718+ {
1719+ var row = model.get_row (model.get_iter_at_row (i));
1720+ var orig_row = backend_model.get_row (backend_model.get_iter_at_row (i));
1721+ assert (row[ResultColumn.URI].equal (orig_row[ResultColumn.URI]));
1722+ assert (row[ResultColumn.CATEGORY].equal (orig_row[ResultColumn.CATEGORY]));
1723+ }
1724+ }
1725+
1726+ public void test_metadata ()
1727+ {
1728+ var metadata = new HashTable<string, Variant> (str_hash, str_equal);
1729+ add_sample_result ("file:///test1", 0, metadata);
1730+
1731+ model.commit_changes ();
1732+ assert (model.get_n_rows () == backend_model.get_n_rows ());
1733+ assert (rows_added == backend_model.get_n_rows ());
1734+ assert (rows_removed == 0);
1735+
1736+ backend_model.clear ();
1737+ // change in metadata will count as completely different result
1738+ metadata["test"] = new Variant.int32 (43);
1739+ add_sample_result ("file:///test1", 0, metadata);
1740+
1741+ rows_added = 0;
1742+ rows_removed = 0;
1743+ model.commit_changes ();
1744+ assert (model.get_n_rows () == backend_model.get_n_rows ());
1745+ assert (rows_added == 1);
1746+ assert (rows_removed == 1);
1747+ // compare the actual values and their ordering
1748+ for (uint i = 0; i < model.get_n_rows (); i++)
1749+ {
1750+ var row = model.get_row (model.get_iter_at_row (i));
1751+ var orig_row = backend_model.get_row (backend_model.get_iter_at_row (i));
1752+ assert (row[ResultColumn.URI].equal (orig_row[ResultColumn.URI]));
1753+ assert (row[ResultColumn.CATEGORY].equal (orig_row[ResultColumn.CATEGORY]));
1754+ assert (row[ResultColumn.METADATA].equal (orig_row[ResultColumn.METADATA]));
1755+ }
1756+ }
1757+ }
1758+ }
1759+}
1760
1761=== modified file 'test/vala/test-vala.vala'
1762--- test/vala/test-vala.vala 2013-06-27 11:13:00 +0000
1763+++ test/vala/test-vala.vala 2013-07-09 13:37:28 +0000
1764@@ -30,6 +30,7 @@
1765 PreferencesSuite preferences_suite;
1766 PreviewSuite preview_suite;
1767 ScopeSuite scope_suite;
1768+ DiffSuite diff_suite;
1769 ScopeDiscoveryTestSuite scope_discovery;
1770 ResultsSynchronizerTestSuite synchronizer_suite;
1771 ScopeGroupTestSuite scope_group;
1772@@ -65,6 +66,9 @@
1773 /* Scope test suite */
1774 scope_suite = new ScopeSuite ();
1775
1776+ /* Diff test suite */
1777+ diff_suite = new DiffSuite ();
1778+
1779 /* Scope discovery test suite */
1780 scope_discovery = new ScopeDiscoveryTestSuite ();
1781

Subscribers

People subscribed via source and target branches