Merge lp:~mhr3/libunity/remove-scopes-via-dconf into lp:libunity
- remove-scopes-via-dconf
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Paweł Stołowski |
Approved revision: | 297 |
Merged at revision: | 289 |
Proposed branch: | lp:~mhr3/libunity/remove-scopes-via-dconf |
Merge into: | lp:libunity |
Diff against target: |
1152 lines (+659/-269) 7 files modified
configure.ac (+1/-1) data/com.canonical.Unity.Lenses.gschema.xml.in.in (+11/-1) debian/changelog (+6/-0) protocol/protocol-scope-discovery.vala (+249/-178) test/data/unity/customized_scopes/masterscope_b.scope (+43/-0) test/data/unity/customized_scopes/masterscope_b/subscope-custom.scope (+12/-0) test/vala/test-scope-discovery.vala (+337/-89) |
To merge this branch: | bzr merge lp:~mhr3/libunity/remove-scopes-via-dconf |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Paweł Stołowski (community) | Approve | ||
PS Jenkins bot (community) | continuous-integration | Approve | |
Review via email: mp+183703@code.launchpad.net |
Commit message
Refactor scope discovery to allow easy OEM customization - removal and locking of scopes via dconf.
Description of the change
Refactor scope discovery to allow easy OEM customization - removal and locking of scopes via dconf.
PS Jenkins bot (ps-jenkins) wrote : | # |
- 291. By Michal Hruby
-
Add a test for the XDG branch of find_scopes
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:291
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 292. By Michal Hruby
-
Add tests for hidden scopes
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:292
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
- 293. By Michal Hruby
-
Bump changelog
- 294. By Michal Hruby
-
Skip non existing directories without throwing an error
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:294
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 295. By Michal Hruby
-
Finish up merging from multiple locations
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:295
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
27 + <key type="as" name="locked-
Why is this part of this MP?
100 + var overriden_dirs = Environment.
I think just SCOPE_DIRECTORIES would be fine?
169 + if (scope_id in hidden_scope_ids)
170 + throw new ParseError.
196 + if (data.id in hidden_scope_ids)
197 + throw new ParseError.
I think we shouldn't be hiding the real cause and instead extend ParseError domain with SCOPE_HIDDEN or so.
265 + yield build_scope_
In theory build_scope_
439 + if (child_
440 + child_node.
...
462 + else
Can you add some debug statements around both cases?
I think that the docstring of find_scopes_for_id needs to be updated?
- 296. By Michal Hruby
-
Fixes based on review comments
Michal Hruby (mhr3) wrote : | # |
> 27 + <key type="as" name="locked-
> Why is this part of this MP?
Both removing scopes via dconf and locking them are features for OEMs.
> 100 + var overriden_dirs = Environment.
> ("LIBUNITY_
> I think just SCOPE_DIRECTORIES would be fine?
I think it's good to have namespaced envvars, it's also consistent with the other envvars (LIBUNITY_
> 169 + if (scope_id in hidden_scope_ids)
> 170 + throw new ParseError.
> scope_id);
>
> 196 + if (data.id in hidden_scope_ids)
> 197 + throw new ParseError.
> data.id);
>
> I think we shouldn't be hiding the real cause and instead extend ParseError
> domain with SCOPE_HIDDEN or so.
I actually wanted to keep it the same, cause a hidden scope should behave exactly the same as if it weren't installed, we wouldn't want to see "... catch (ParseError.
> 265 + yield build_scope_
> In theory build_scope_
> directories unless I overlooked something), so can you try-catch here just to
> be on the safe side (we should never break this loop even if single directory
> fails)?
>
> 439 + if (child_
> 440 + child_node.
> ...
> 462 + else
> Can you add some debug statements around both cases?
Added for the if, the else recurses, so it's logged already.
> I think that the docstring of find_scopes_for_id needs to be updated?
Not really, still applies.
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:296
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
> > 27 + <key type="as" name="locked-
> > Why is this part of this MP?
>
> Both removing scopes via dconf and locking them are features for OEMs.
Ok, please update commit message then ;).
> I actually wanted to keep it the same, cause a hidden scope should behave
> exactly the same as if it weren't installed, we wouldn't want to see "...
> catch (ParseError.
> about it }"
How about changing error message then? That will be useful when looking at logs.
Michal Hruby (mhr3) wrote : | # |
> Ok, please update commit message then ;).
Updated.
> How about changing error message then? That will be useful when looking at
> logs.
That still makes the special casing possible.
- 297. By Michal Hruby
-
Change error message
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:297
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Paweł Stołowski (stolowski) wrote : | # |
Looks good, thanks!
Preview Diff
1 | === modified file 'configure.ac' |
2 | --- configure.ac 2013-08-27 14:20:54 +0000 |
3 | +++ configure.ac 2013-09-06 15:44:22 +0000 |
4 | @@ -1,5 +1,5 @@ |
5 | # When releasing also remember to update the soname as instructed below |
6 | -AC_INIT(libunity, 7.1.0) |
7 | +AC_INIT(libunity, 7.1.1) |
8 | |
9 | AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) |
10 | AM_CONFIG_HEADER(config.h) |
11 | |
12 | === modified file 'data/com.canonical.Unity.Lenses.gschema.xml.in.in' |
13 | --- data/com.canonical.Unity.Lenses.gschema.xml.in.in 2013-05-08 15:11:34 +0000 |
14 | +++ data/com.canonical.Unity.Lenses.gschema.xml.in.in 2013-09-06 15:44:22 +0000 |
15 | @@ -14,7 +14,17 @@ |
16 | <key type="as" name="disabled-scopes"> |
17 | <default>[]</default> |
18 | <summary>Scopes that will be ignored during queries.</summary> |
19 | - <description>List of scope IDs that will be ignored when master scopes are querying their subscopes.</description> |
20 | + <description>List of scope IDs that will be ignored when master scopes are querying their subscopes (this list changes when user enables or disables a scope).</description> |
21 | + </key> |
22 | + <key type="as" name="hidden-scopes"> |
23 | + <default>[]</default> |
24 | + <summary>Scopes that are blacklisted.</summary> |
25 | + <description>List of scope IDs that will be considered the same as if they were not installed at all.</description> |
26 | + </key> |
27 | + <key type="as" name="locked-scopes"> |
28 | + <default>[]</default> |
29 | + <summary>Scopes that cannot be enabled nor disabled by the user.</summary> |
30 | + <description>List of scope IDs that the user will see as installed and won't be able to enable nor disable.</description> |
31 | </key> |
32 | <key type="as" name="home-lens-priority"> |
33 | <default>['files.scope', 'music.scope' ]</default> |
34 | |
35 | === modified file 'debian/changelog' |
36 | --- debian/changelog 2013-08-28 13:23:31 +0000 |
37 | +++ debian/changelog 2013-09-06 15:44:22 +0000 |
38 | @@ -1,3 +1,9 @@ |
39 | +libunity (7.1.1-0ubuntu1) UNRELEASED; urgency=low |
40 | + |
41 | + * Allow permanent scope "removal" just by using dconf. |
42 | + |
43 | + -- Michal Hruby <michal.hruby@canonical.com> Wed, 04 Sep 2013 10:05:06 +0100 |
44 | + |
45 | libunity (7.1.0+13.10.20130828.1-0ubuntu1) saucy; urgency=low |
46 | |
47 | [ Pawel Stolowski ] |
48 | |
49 | === modified file 'protocol/protocol-scope-discovery.vala' |
50 | --- protocol/protocol-scope-discovery.vala 2013-08-06 08:09:15 +0000 |
51 | +++ protocol/protocol-scope-discovery.vala 2013-09-06 15:44:22 +0000 |
52 | @@ -189,7 +189,10 @@ |
53 | private static const string SCOPE_GROUP = "Scope"; |
54 | private static const string DESKTOP_GROUP = "Desktop Entry"; |
55 | |
56 | + private static string[] scope_directories = null; |
57 | private static string[] scope_file_prefixes = null; |
58 | + private static bool overrides_scope_dirs = false; |
59 | + |
60 | private static string extract_scope_id (string path) |
61 | { |
62 | string? real_path = null; |
63 | @@ -200,17 +203,7 @@ |
64 | path = real_path; |
65 | } |
66 | |
67 | - if (scope_file_prefixes == null) |
68 | - { |
69 | - foreach (unowned string data_dir in Environment.get_system_data_dirs ()) |
70 | - { |
71 | - var sys_path = Path.build_path (Path.DIR_SEPARATOR_S, |
72 | - data_dir, SCOPES_DIR, null); |
73 | - var f = File.new_for_path (sys_path); |
74 | - // this will resolve relative paths |
75 | - scope_file_prefixes += "%s/".printf (f.get_path ()); |
76 | - } |
77 | - } |
78 | + if (scope_file_prefixes == null) init_scope_file_prefixes (); |
79 | |
80 | string normalized_path = path.replace ("//", "/"); |
81 | |
82 | @@ -230,9 +223,54 @@ |
83 | return Path.get_basename (path); |
84 | } |
85 | |
86 | + internal static void init_scope_file_prefixes () |
87 | + { |
88 | + if (scope_directories == null) init_scope_directories (); |
89 | + scope_file_prefixes = {}; |
90 | + foreach (unowned string scope_dir in scope_directories) |
91 | + { |
92 | + var f = File.new_for_path (scope_dir); |
93 | + // this will resolve relative paths |
94 | + scope_file_prefixes += "%s/".printf (f.get_path ()); |
95 | + } |
96 | + } |
97 | + |
98 | + internal static void init_scope_directories () |
99 | + { |
100 | + var overriden_dirs = Environment.get_variable ("LIBUNITY_SCOPE_DIRECTORIES"); |
101 | + overrides_scope_dirs = overriden_dirs != null; |
102 | + if (overriden_dirs == null) |
103 | + { |
104 | + scope_directories = {}; |
105 | + var data_dirs = Environment.get_system_data_dirs (); |
106 | + foreach (unowned string data_dir in data_dirs) |
107 | + { |
108 | + scope_directories += Path.build_filename (data_dir, SCOPES_DIR); |
109 | + } |
110 | + } |
111 | + else |
112 | + { |
113 | + scope_directories = overriden_dirs.split (":"); |
114 | + } |
115 | + } |
116 | + |
117 | |
118 | public class ScopeMetadata |
119 | { |
120 | + private static string[] hidden_scope_ids; |
121 | + static construct |
122 | + { |
123 | + // the hidden scopes are not supposed to be changed, so no need |
124 | + // to monitor for dynamic changes |
125 | + update_hidden_scope_ids (); |
126 | + } |
127 | + |
128 | + internal static void update_hidden_scope_ids () |
129 | + { |
130 | + var settings = new Settings ("com.canonical.Unity.Lenses"); |
131 | + hidden_scope_ids = settings.get_strv ("hidden-scopes"); |
132 | + } |
133 | + |
134 | /* careful here, although it's a private lib, keeping it |
135 | * ABI compatible is a good idea (unity-core uses it) */ |
136 | public string id; |
137 | @@ -265,6 +303,7 @@ |
138 | private CategoryDefinition[] categories; |
139 | private FilterDefinition[] filters; |
140 | private string[] subscope_ids; |
141 | + private bool overrides_subscopes; |
142 | |
143 | public unowned CategoryDefinition[] get_categories () |
144 | { |
145 | @@ -280,6 +319,11 @@ |
146 | return subscope_ids; |
147 | } |
148 | |
149 | + internal bool get_overrides_subscopes () |
150 | + { |
151 | + return overrides_subscopes; |
152 | + } |
153 | + |
154 | public void load_from_key_file (KeyFile file) throws Error |
155 | { |
156 | this.domain = null; |
157 | @@ -403,6 +447,7 @@ |
158 | if (file.has_key (SCOPE_GROUP, "Subscopes")) |
159 | { |
160 | this.subscope_ids = file.get_string_list (SCOPE_GROUP, "Subscopes"); |
161 | + this.overrides_subscopes = true; |
162 | } |
163 | |
164 | const string FILTER_PREFIX = "Filter "; |
165 | @@ -505,11 +550,23 @@ |
166 | { |
167 | debug ("for_id: %s", scope_id); |
168 | |
169 | + if (scope_id in hidden_scope_ids) |
170 | + throw new ParseError.FILE_NOT_FOUND ("Scope is disabled: %s", scope_id); |
171 | + |
172 | string full_path; |
173 | var file = new KeyFile (); |
174 | - var path = "%s/%s".printf (SCOPES_DIR, scope_id); |
175 | - bool loaded = file.load_from_data_dirs (path, out full_path, |
176 | - KeyFileFlags.NONE); |
177 | + bool loaded; |
178 | + if (overrides_scope_dirs) |
179 | + { |
180 | + loaded = file.load_from_dirs (scope_id, scope_directories, |
181 | + out full_path, KeyFileFlags.NONE); |
182 | + } |
183 | + else |
184 | + { |
185 | + var path = "%s/%s".printf (SCOPES_DIR, scope_id); |
186 | + loaded = file.load_from_data_dirs (path, out full_path, |
187 | + KeyFileFlags.NONE); |
188 | + } |
189 | |
190 | if (!loaded) |
191 | throw new ParseError.FILE_NOT_FOUND ("Scope not found: %s", scope_id); |
192 | @@ -546,6 +603,9 @@ |
193 | data.full_path = full_path; |
194 | data.id = extract_scope_id (full_path); |
195 | |
196 | + if (data.id in hidden_scope_ids) |
197 | + throw new ParseError.FILE_NOT_FOUND ("Scope is disabled: %s", data.id); |
198 | + |
199 | return data; |
200 | } |
201 | } |
202 | @@ -568,123 +628,74 @@ |
203 | { |
204 | } |
205 | |
206 | + static construct |
207 | + { |
208 | + init_scope_directories (); |
209 | + } |
210 | + |
211 | /** |
212 | - * Process .scope file and scopes in a subdirectory if it's master scope. |
213 | + * Build registry of all scopes in start_path. |
214 | + * start_path can be a directory, a .scope file path or just scope id (.scope file name, including extenstion). |
215 | * |
216 | - * @param filename path of a .scope file |
217 | + * @param start_path starting directory or specific .scope file |
218 | + * @return registry of all scopes (if start_path is a dir) or just one scope and its subscopes. |
219 | */ |
220 | - private async void process_scope (string filename) throws Error |
221 | + public static async ScopeRegistry find_scopes (string? start_path) throws Error |
222 | { |
223 | - debug ("process_scope: %s", filename); |
224 | - |
225 | - ScopeMetadata scope_data = ScopeMetadata.for_path (filename); // this may throw, in such case don't process this scope (and possibly its subscopes if it was master scope) |
226 | - if (scope_data == null) return; |
227 | - |
228 | - var scope_node = new ScopeRegistryNode () |
229 | - { |
230 | - scope_info = scope_data, |
231 | - sub_scopes = null |
232 | - }; |
233 | - |
234 | - // adding the scope_node now, but we'll update its fields if it's a master |
235 | - scopes_.append (scope_node); |
236 | - |
237 | - if (!scope_data.is_master) return; |
238 | - |
239 | - /* This is a master scope, find its children */ |
240 | - |
241 | - // does the master scope hardcode its children? |
242 | - unowned string[] subscopes = scope_data.get_subscope_ids (); |
243 | - if (subscopes.length > 0) |
244 | - { |
245 | - foreach (unowned string subscope_id in subscopes) |
246 | + var registry = new ScopeRegistry (); |
247 | + Node<ScopeMetadata?> root_node = new Node<ScopeMetadata?> (null); |
248 | + if (start_path == null) |
249 | + { |
250 | + foreach (unowned string scope_dir in scope_directories) |
251 | { |
252 | + if (!FileUtils.test (scope_dir, FileTest.IS_DIR)) continue; |
253 | try |
254 | { |
255 | - ScopeMetadata subscope_data = ScopeMetadata.for_id (subscope_id); |
256 | - if (scope_node.sub_scopes == null) |
257 | - scope_node.sub_scopes = new GLib.SList<ScopeMetadata?> (); |
258 | - |
259 | - scope_node.sub_scopes.append (subscope_data); |
260 | + yield build_scope_node_tree (root_node, scope_dir); |
261 | } |
262 | catch (Error e) |
263 | { |
264 | - warning ("Failed to process '%s': %s", subscope_id, e.message); |
265 | + // we'll ignore errors from here |
266 | + warning ("Unable to process scope directory %s: %s", |
267 | + scope_dir, e.message); |
268 | } |
269 | } |
270 | } |
271 | else |
272 | { |
273 | - // check if there is a subdirectory with the name of master scope. |
274 | - // note that failure on single sub-scope shouldn't disable master & valid sub-scopes. |
275 | - var scopefile = GLib.File.new_for_path (scope_data.full_path); |
276 | - string scope_name = remove_scope_extension (scope_data.id); |
277 | - var parent = scopefile.get_parent (); |
278 | - if (parent == null) return; |
279 | - |
280 | - string check_path = Path.build_filename (parent.get_path (), scope_name); |
281 | - |
282 | - if (!FileUtils.test (check_path, FileTest.IS_DIR)) return; |
283 | - |
284 | - GLib.Dir? scope_subdir = null; |
285 | - try |
286 | - { |
287 | - scope_subdir = GLib.Dir.open (check_path); |
288 | - } |
289 | - catch (FileError e) |
290 | - { |
291 | - warning ("Error accessing directory %s", check_path); |
292 | - } |
293 | - |
294 | - if (scope_subdir == null) return; |
295 | - |
296 | - // iterate over all files, find .*scope |
297 | - string slave_scope_filename; |
298 | - while ((slave_scope_filename = scope_subdir.read_name ()) != null) |
299 | - { |
300 | - slave_scope_filename = Path.build_filename (check_path, slave_scope_filename); |
301 | - try |
302 | - { |
303 | - ScopeMetadata slave_scope_data = ScopeMetadata.for_path (slave_scope_filename); |
304 | - if (scope_node.sub_scopes == null) |
305 | - scope_node.sub_scopes = new GLib.SList<ScopeMetadata?> (); |
306 | - |
307 | - scope_node.sub_scopes.append (slave_scope_data); |
308 | - } |
309 | - catch (Error e) |
310 | - { |
311 | - warning ("Failed to process '%s': %s", slave_scope_filename, e.message); |
312 | - } |
313 | - } |
314 | + yield build_scope_node_tree (root_node, start_path); |
315 | } |
316 | - } |
317 | - |
318 | - /** |
319 | - * Build registry of all scopes in start_path. |
320 | - * start_path can be a directory, a .scope file path or just scope id (.scope file name, including extenstion). |
321 | - * |
322 | - * @param start_path starting directory or specific .scope file |
323 | - * @return registry of all scopes (if start_path is a dir) or just one scope and its subscopes. |
324 | - */ |
325 | - public static async ScopeRegistry find_scopes (string start_path) throws Error |
326 | - { |
327 | - var reg = yield find_scopes_for_master (start_path); |
328 | - return reg; |
329 | - } |
330 | - |
331 | - /** |
332 | - * Build registry of all scopes in start_path. |
333 | - * start_path can be a directory, a .scope file path or just scope id (.scope file name, including extenstion). |
334 | - * |
335 | - * @param start_path starting directory or specific .scope file |
336 | - * @return registry of all scopes (if start_path is a dir) or just one scope and its subscopes. |
337 | - */ |
338 | - private static async ScopeRegistry find_scopes_for_master (string start_path) throws Error |
339 | - { |
340 | - debug ("find_scopes_for_master: %s\n", start_path); |
341 | - |
342 | - var reg = new ScopeRegistry (); |
343 | - |
344 | + |
345 | + registry.from_tree (root_node); |
346 | + return registry; |
347 | + } |
348 | + |
349 | + private static bool node_has_child_with_id (Node<ScopeMetadata> node, |
350 | + string scope_id, |
351 | + out unowned Node<ScopeMetadata> child) |
352 | + { |
353 | + bool scope_id_present = false; |
354 | + unowned Node<ScopeMetadata> found_child = null; |
355 | + node.children_foreach (TraverseFlags.ALL, (child_) => |
356 | + { |
357 | + unowned Node<ScopeMetadata> child_node = child_; |
358 | + if (child_node.data.id == scope_id) |
359 | + { |
360 | + scope_id_present = true; |
361 | + found_child = child_node; |
362 | + } |
363 | + }); |
364 | + |
365 | + child = found_child; |
366 | + |
367 | + return scope_id_present; |
368 | + } |
369 | + |
370 | + private static async void build_scope_node_tree (Node<ScopeMetadata> root_node, |
371 | + string start_path) |
372 | + throws Error |
373 | + { |
374 | + debug ("build_scope_node_tree [level: %u]: %s", root_node.depth (), start_path); |
375 | if (FileUtils.test (start_path, FileTest.IS_DIR)) |
376 | { |
377 | var dir = GLib.Dir.open (start_path); |
378 | @@ -693,33 +704,108 @@ |
379 | while ((name = dir.read_name ()) != null) |
380 | { |
381 | string filename = Path.build_filename (start_path, name); |
382 | - if (filename.has_suffix (".scope")) |
383 | - { |
384 | - debug ("Found scope file: %s", filename); |
385 | - try // failure of single scope shouldn't break processing of others scopes |
386 | - { |
387 | - yield reg.process_scope (filename); |
388 | - } |
389 | - catch (Error e) |
390 | - { |
391 | - warning ("Failed to process '%s': %s", filename, e.message); |
392 | - } |
393 | + if (!filename.has_suffix (".scope")) continue; |
394 | + // failure of single scope shouldn't break processing of others scopes |
395 | + try |
396 | + { |
397 | + yield build_scope_node_tree (root_node, filename); |
398 | + } |
399 | + catch (Error e) |
400 | + { |
401 | + warning ("Failed to process '%s': %s", filename, e.message); |
402 | } |
403 | } |
404 | } |
405 | else |
406 | { |
407 | - if (start_path.has_suffix (".scope")) |
408 | - { |
409 | - yield reg.process_scope (start_path); // no try-catch here, as request for specific scope file should fail on error |
410 | - } |
411 | - else |
412 | - { |
413 | - throw new ParseError.UNKNOWN_FILE ("Unknown file type"); |
414 | + if (!start_path.has_suffix (".scope")) |
415 | + { |
416 | + throw new ParseError.UNKNOWN_FILE ("Unknown file type: \"%s\"", start_path); |
417 | + } |
418 | + debug ("Found scope file: %s", start_path); |
419 | + // this may throw, in such case don't process this scope |
420 | + // (and possibly its subscopes if it was master scope) |
421 | + ScopeMetadata? scope_data = ScopeMetadata.for_path (start_path); |
422 | + if (scope_data == null) return; |
423 | + |
424 | + // do we already have this scope in the tree? |
425 | + unowned Node<ScopeMetadata> child_node = null; |
426 | + bool scope_id_present = node_has_child_with_id (root_node, |
427 | + scope_data.id, |
428 | + out child_node); |
429 | + |
430 | + if (!scope_id_present) |
431 | + { |
432 | + // save the node in the tree |
433 | + child_node = root_node.append_data (scope_data); |
434 | + } |
435 | + |
436 | + assert (child_node != null); |
437 | + |
438 | + if (child_node.data.is_master) |
439 | + { |
440 | + // if master scope specifies its subscopes, don't try to merge |
441 | + // the children from multiple locations |
442 | + if (child_node.data.get_overrides_subscopes () && |
443 | + child_node.n_children () == 0) |
444 | + { |
445 | + debug ("Scope %s overrides its children", child_node.data.id); |
446 | + unowned string[] subscopes = child_node.data.get_subscope_ids (); |
447 | + foreach (unowned string subscope_id in subscopes) |
448 | + { |
449 | + try |
450 | + { |
451 | + ScopeMetadata subscope_data = ScopeMetadata.for_id (subscope_id); |
452 | + if (subscope_data != null && |
453 | + !node_has_child_with_id (child_node, subscope_data.id, null)) |
454 | + { |
455 | + // FIXME: we're not building the complete tree here, |
456 | + // but right now it's not needed |
457 | + child_node.append_data (subscope_data); |
458 | + } |
459 | + } |
460 | + catch (Error e) |
461 | + { |
462 | + warning ("Failed to process '%s': %s", subscope_id, e.message); |
463 | + } |
464 | + } |
465 | + } |
466 | + else |
467 | + { |
468 | + /* This is a master scope, find its children in subdirectory */ |
469 | + var scopefile = GLib.File.new_for_path (scope_data.full_path); |
470 | + var parent = scopefile.get_parent (); |
471 | + if (parent == null) return; |
472 | + string scope_name = remove_scope_extension (scope_data.id); |
473 | + |
474 | + string check_path = Path.build_filename (parent.get_path (), scope_name); |
475 | + if (!FileUtils.test (check_path, FileTest.IS_DIR)) return; |
476 | + yield build_scope_node_tree (child_node, check_path); |
477 | + } |
478 | } |
479 | } |
480 | - debug ("find_scopes_for_master finished"); |
481 | - return reg; |
482 | + } |
483 | + |
484 | + private void from_tree (Node<ScopeMetadata> root_node) |
485 | + { |
486 | + // only 2 level traverse, anything deeper will be discarded |
487 | + root_node.children_foreach (TraverseFlags.ALL, (top_child_) => |
488 | + { |
489 | + unowned Node<ScopeMetadata> top_child = top_child_; |
490 | + var scope_node = new ScopeRegistryNode () |
491 | + { |
492 | + scope_info = top_child.data, |
493 | + sub_scopes = new GLib.SList<ScopeMetadata?> () |
494 | + }; |
495 | + |
496 | + top_child.children_foreach (TraverseFlags.ALL, (child_) => |
497 | + { |
498 | + unowned Node<ScopeMetadata> child = child_; |
499 | + scope_node.sub_scopes.append (child.data); |
500 | + }); |
501 | + |
502 | + this.scopes_.append (scope_node); |
503 | + }); |
504 | } |
505 | |
506 | internal static string remove_scope_extension (string scope_id) |
507 | @@ -738,64 +824,49 @@ |
508 | */ |
509 | public static async ScopeRegistry find_scopes_for_id (string scope_id, string? root_path = null) throws Error |
510 | { |
511 | + var registry = new ScopeRegistry (); |
512 | debug ("find_scopes_for_id: %s", scope_id); |
513 | |
514 | - string[]? dirs = null; |
515 | - if (root_path == null) |
516 | - { |
517 | - dirs = GLib.Environment.get_system_data_dirs (); |
518 | - } |
519 | - else |
520 | - { |
521 | - dirs = new string [1] {root_path}; |
522 | - } |
523 | - |
524 | - if (dirs == null || dirs.length == 0) |
525 | - { |
526 | - throw new ParseError.INVALID_PATH ("Invalid scopes path"); |
527 | - } |
528 | - |
529 | + var root_node = new Node<ScopeMetadata?> (null); |
530 | // try to find the master scope file |
531 | try |
532 | { |
533 | - var master_scope_metadata = ScopeMetadata.for_id (scope_id); |
534 | - unowned string[] subscopes = master_scope_metadata.get_subscope_ids (); |
535 | - if (subscopes.length > 0) |
536 | - { |
537 | - var reg = new ScopeRegistry (); |
538 | - foreach (unowned string subscope_id in subscopes) |
539 | - { |
540 | - var node = new ScopeRegistryNode (); |
541 | - try |
542 | - { |
543 | - node.scope_info = ScopeMetadata.for_id (subscope_id); |
544 | - reg.scopes_.append (node); |
545 | - } |
546 | - catch (Error err) |
547 | - { |
548 | - } |
549 | - } |
550 | - return reg; |
551 | - } |
552 | + var scope_metadata = ScopeMetadata.for_id (scope_id); |
553 | + yield build_scope_node_tree (root_node, scope_metadata.full_path); |
554 | + if (scope_file_prefixes == null) init_scope_file_prefixes (); |
555 | } |
556 | catch (Error err) |
557 | { |
558 | // silently ignore |
559 | } |
560 | |
561 | - var sc = remove_scope_extension (scope_id); |
562 | + /* if the scope was found, we need to check also the other |
563 | + * scope_directories, and merge sub-scopes from all of them, if it wasn't |
564 | + * this wasn't a valid scope_id, perhaps we can find a dir? */ |
565 | + string[]? dirs = root_path == null ? |
566 | + scope_directories : new string [1] { root_path }; |
567 | + |
568 | + if (dirs == null || dirs.length == 0) |
569 | + { |
570 | + throw new ParseError.INVALID_PATH ("Invalid scopes path"); |
571 | + } |
572 | + |
573 | + unowned Node<ScopeMetadata?> node = root_node.n_children () > 0 ? |
574 | + root_node.first_child () : root_node; |
575 | + |
576 | + var suffix = remove_scope_extension (scope_id); |
577 | foreach (var path in dirs) |
578 | { |
579 | - var check_path = root_path == null ? Path.build_path (Path.DIR_SEPARATOR_S, path, SCOPES_DIR, sc) : Path.build_path (Path.DIR_SEPARATOR_S, root_path, sc); |
580 | - if (FileUtils.test (check_path, FileTest.IS_DIR)) |
581 | - { |
582 | - var reg = yield find_scopes_for_master (check_path); |
583 | - return reg; |
584 | - } |
585 | + var check_path = Path.build_filename (path, suffix); |
586 | + if (!FileUtils.test (check_path, FileTest.IS_DIR)) continue; |
587 | + |
588 | + yield build_scope_node_tree (node, check_path); |
589 | } |
590 | |
591 | - debug ("No sub-scopes directory found for master scope %s", scope_id); |
592 | - return new ScopeRegistry (); |
593 | + // the registry from this method is not expected to contain the scope |
594 | + // itself, so don't use the root directly |
595 | + registry.from_tree (node); |
596 | + return registry; |
597 | } |
598 | } |
599 | |
600 | |
601 | === added directory 'test/data/unity/customized_scopes' |
602 | === added directory 'test/data/unity/customized_scopes/masterscope_b' |
603 | === added file 'test/data/unity/customized_scopes/masterscope_b.scope' |
604 | --- test/data/unity/customized_scopes/masterscope_b.scope 1970-01-01 00:00:00 +0000 |
605 | +++ test/data/unity/customized_scopes/masterscope_b.scope 2013-09-06 15:44:22 +0000 |
606 | @@ -0,0 +1,43 @@ |
607 | +[Scope] |
608 | +GroupName=com.canonical.Unity.Scope.MasterB |
609 | +UniqueName=/com/canonical/unity/scope/masterb |
610 | +Icon=/usr/share/unity/6/icon2.svg |
611 | +IsMaster=true |
612 | +RequiredMetadata=bid[s];a[s];b[y]; |
613 | +OptionalMetadata= |
614 | +# Keywords is localized |
615 | +Keywords=misc;booze; |
616 | +Type=booze |
617 | +QueryPattern=^( |
618 | +Name=Sample Master Scope 2 |
619 | +Description=Find even more stuff |
620 | +SearchHint=Search things |
621 | +Shortcut=w |
622 | +NoContentHint=Sorry to make you a sad panda, but there's nothing here. |
623 | + |
624 | +[Desktop Entry] |
625 | +X-Ubuntu-Gettext-Domain=fakedomain |
626 | + |
627 | +[Category cat1] |
628 | +Name=Category #1 |
629 | +Icon=/usr/share/unity/category1_icon.svg |
630 | +DedupField=uri |
631 | + |
632 | +[Category cat2] |
633 | +Name=Category #2 |
634 | +Icon=/usr/share/unity/category2_icon.svg |
635 | +Renderer=list |
636 | +ContentType=music |
637 | +SortField=title |
638 | + |
639 | +[Filter genre] |
640 | +Name=Genre |
641 | +Type=check-options |
642 | +OptionIDs=bluez;pop;jazz;electro; |
643 | +OptionNames=Bluez;Pop;Jazz;Electro; |
644 | + |
645 | +[Filter decade] |
646 | +Name=Decade |
647 | +Type=multi-range |
648 | +OptionIDs=70;80;90; |
649 | +OptionNames=70s;80s;90s; |
650 | |
651 | === added file 'test/data/unity/customized_scopes/masterscope_b/subscope-custom.scope' |
652 | --- test/data/unity/customized_scopes/masterscope_b/subscope-custom.scope 1970-01-01 00:00:00 +0000 |
653 | +++ test/data/unity/customized_scopes/masterscope_b/subscope-custom.scope 2013-09-06 15:44:22 +0000 |
654 | @@ -0,0 +1,12 @@ |
655 | +[Scope] |
656 | +GroupName=com.canonical.Unity.Scope.SubscopeCustom |
657 | +UniqueName=/com/canonical/unity/scope/subscope-custom |
658 | +Icon=/usr/share/unity/icon-sub.svg |
659 | +RequiredMetadata=bid[s]; |
660 | +OptionalMetadata= |
661 | +Keywords=misc; |
662 | +Type=varia |
663 | +QueryPattern=^@ |
664 | +Name=Sample custom subscope |
665 | +Description=Find various stuff subscope |
666 | +SearchHint=Search stuff subscope |
667 | |
668 | === modified file 'test/vala/test-scope-discovery.vala' |
669 | --- test/vala/test-scope-discovery.vala 2013-07-30 16:15:18 +0000 |
670 | +++ test/vala/test-scope-discovery.vala 2013-09-06 15:44:22 +0000 |
671 | @@ -18,6 +18,11 @@ |
672 | */ |
673 | using Unity; |
674 | |
675 | +// a small hack for a bunch of internal protolib functions, that are useful |
676 | +public extern void unity_protocol_scope_registry_scope_metadata_update_hidden_scope_ids (); |
677 | +public extern void unity_protocol_scope_registry_init_scope_directories (); |
678 | +public extern void unity_protocol_scope_registry_init_scope_file_prefixes (); |
679 | + |
680 | namespace Unity.Test |
681 | { |
682 | public class ScopeDiscoveryTestSuite |
683 | @@ -25,15 +30,21 @@ |
684 | public static const string SCOPES_ROOT = Config.TESTDIR + "/data/unity/scopes"; |
685 | const string PROTO_DOMAIN = "libunity-protocol-private"; |
686 | |
687 | + const int NUM_TOP_LEVEL_TEST_SCOPES = 4; |
688 | + |
689 | public ScopeDiscoveryTestSuite () |
690 | { |
691 | GLib.Environment.set_variable ("XDG_DATA_DIRS", Config.TESTDIR + "/data", true); |
692 | + GLib.Environment.set_variable ("LIBUNITY_SCOPE_DIRECTORIES", Config.TESTDIR + "/data/unity/scopes", true); |
693 | |
694 | GLib.Test.add_data_func ("/Unit/ScopeMetadata/LoadScopeById", ScopeDiscoveryTestSuite.test_metadata_load_by_scope_id); |
695 | GLib.Test.add_data_func ("/Unit/ScopeMetadata/LoadMasterscope", ScopeDiscoveryTestSuite.test_metadata_load_masterscope); |
696 | GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopes", ScopeDiscoveryTestSuite.test_find_all_scopes); |
697 | + GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopesInXdg", ScopeDiscoveryTestSuite.test_find_all_scopes_xdg); |
698 | + GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopesWithHidden", ScopeDiscoveryTestSuite.test_find_all_scopes_with_hidden_scopes); |
699 | GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindSpecificScopes", ScopeDiscoveryTestSuite.test_find_specific_scope); |
700 | GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindScopeById", ScopeDiscoveryTestSuite.test_find_scope_by_id); |
701 | + GLib.Test.add_data_func ("/Unit/ScopeDiscovery/FindOverriddenScopeById", ScopeDiscoveryTestSuite.test_find_overridden_scope_by_id); |
702 | } |
703 | |
704 | internal static void check_scope_b_subscopes (Protocol.ScopeRegistry.ScopeRegistryNode node) |
705 | @@ -94,90 +105,260 @@ |
706 | Protocol.ScopeRegistry reg = null; |
707 | |
708 | var ml = new MainLoop (); |
709 | - Protocol.ScopeRegistry.find_scopes.begin (SCOPES_ROOT, |
710 | - (obj, res) => |
711 | - { |
712 | - reg = Protocol.ScopeRegistry.find_scopes.end (res); |
713 | - ml.quit (); |
714 | - }); |
715 | - |
716 | - run_with_timeout (ml, 5000); |
717 | - |
718 | - assert (reg != null); |
719 | - assert (reg.scopes != null); |
720 | - assert (reg.scopes.length () >= 2); // there are exactly 2 top-level scopes (master scopes) |
721 | - |
722 | - bool got_masterscope_1 = false; |
723 | - bool got_masterscope_2 = false; |
724 | - bool got_masterscope_3 = false; |
725 | - |
726 | - foreach (var node in reg.scopes) |
727 | - { |
728 | - assert (node != null); |
729 | - var inf = node.scope_info; |
730 | - if (inf.name == "Sample Master Scope 1") |
731 | - { |
732 | - assert (inf.id == "masterscope_a.scope"); |
733 | - assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterA"); |
734 | - assert (inf.dbus_path == "/com/canonical/unity/scope/mastera"); |
735 | - assert (inf.is_master == true); |
736 | - assert (inf.icon == "/usr/share/unity/6/icon1.svg"); |
737 | - assert (inf.required_metadata.columns.length == 3); |
738 | - var col = inf.required_metadata.columns[0]; |
739 | - assert (col.name == "some_id"); |
740 | - assert (col.type_id == "s"); |
741 | - col = inf.required_metadata.columns[1]; |
742 | - assert (col.name == "foo"); |
743 | - assert (col.type_id == "s"); |
744 | - col = inf.required_metadata.columns[2]; |
745 | - assert (col.name == "bar"); |
746 | - assert (col.type_id == "y"); |
747 | - assert (inf.optional_metadata != null); |
748 | - assert (inf.optional_metadata.columns.length == 1); |
749 | - col = inf.optional_metadata.columns[0]; |
750 | - assert (col.name == "baz"); |
751 | - assert (col.type_id == "u"); |
752 | - assert (inf.keywords.length () == 1); |
753 | - assert (inf.type == "varia"); |
754 | - assert (inf.query_pattern == ""); |
755 | - assert (inf.description == "Find various stuff"); |
756 | - assert (inf.search_hint == "Search stuff"); |
757 | - assert (node.sub_scopes == null); |
758 | - |
759 | - got_masterscope_1 = true; |
760 | - } |
761 | - else if (inf.name == "Sample Master Scope 2") |
762 | - { |
763 | - assert (inf.id == "masterscope_b.scope"); |
764 | - assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterB"); |
765 | - assert (inf.dbus_path == "/com/canonical/unity/scope/masterb"); |
766 | - assert (inf.is_master == true); |
767 | - assert (inf.icon == "/usr/share/unity/6/icon2.svg"); |
768 | - assert (inf.keywords.length () == 2); |
769 | - assert (inf.keywords.nth_data (0) == "misc"); |
770 | - assert (inf.keywords.nth_data (1) == "booze"); |
771 | - assert (inf.type == "booze"); |
772 | - assert (inf.query_pattern == "^("); |
773 | - assert (inf.description == "Find even more stuff"); |
774 | - assert (inf.search_hint == "Search things"); |
775 | - |
776 | - check_scope_b_subscopes (node); |
777 | - |
778 | - got_masterscope_2 = true; |
779 | - } |
780 | - else if (inf.name == "Sample Master Scope 3") |
781 | - { |
782 | - assert (inf.id == "masterscope_c.scope"); |
783 | - |
784 | - assert (node.sub_scopes.length () == 1); |
785 | - assert (node.sub_scopes.nth_data (0).id == "test_masterscope.scope"); |
786 | - got_masterscope_3 = true; |
787 | - } |
788 | - } |
789 | - |
790 | - assert (got_masterscope_1 == true); |
791 | - assert (got_masterscope_2 == true); |
792 | - assert (got_masterscope_3 == true); |
793 | + Protocol.ScopeRegistry.find_scopes.begin (SCOPES_ROOT, (obj, res) => |
794 | + { |
795 | + reg = Protocol.ScopeRegistry.find_scopes.end (res); |
796 | + ml.quit (); |
797 | + }); |
798 | + |
799 | + run_with_timeout (ml, 5000); |
800 | + |
801 | + assert (reg != null); |
802 | + assert (reg.scopes != null); |
803 | + assert (reg.scopes.length () == NUM_TOP_LEVEL_TEST_SCOPES); |
804 | + |
805 | + bool got_masterscope_1 = false; |
806 | + bool got_masterscope_2 = false; |
807 | + bool got_masterscope_3 = false; |
808 | + |
809 | + foreach (var node in reg.scopes) |
810 | + { |
811 | + assert (node != null); |
812 | + var inf = node.scope_info; |
813 | + if (inf.name == "Sample Master Scope 1") |
814 | + { |
815 | + assert (inf.id == "masterscope_a.scope"); |
816 | + assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterA"); |
817 | + assert (inf.dbus_path == "/com/canonical/unity/scope/mastera"); |
818 | + assert (inf.is_master == true); |
819 | + assert (inf.icon == "/usr/share/unity/6/icon1.svg"); |
820 | + assert (inf.required_metadata.columns.length == 3); |
821 | + var col = inf.required_metadata.columns[0]; |
822 | + assert (col.name == "some_id"); |
823 | + assert (col.type_id == "s"); |
824 | + col = inf.required_metadata.columns[1]; |
825 | + assert (col.name == "foo"); |
826 | + assert (col.type_id == "s"); |
827 | + col = inf.required_metadata.columns[2]; |
828 | + assert (col.name == "bar"); |
829 | + assert (col.type_id == "y"); |
830 | + assert (inf.optional_metadata != null); |
831 | + assert (inf.optional_metadata.columns.length == 1); |
832 | + col = inf.optional_metadata.columns[0]; |
833 | + assert (col.name == "baz"); |
834 | + assert (col.type_id == "u"); |
835 | + assert (inf.keywords.length () == 1); |
836 | + assert (inf.type == "varia"); |
837 | + assert (inf.query_pattern == ""); |
838 | + assert (inf.description == "Find various stuff"); |
839 | + assert (inf.search_hint == "Search stuff"); |
840 | + assert (node.sub_scopes == null); |
841 | + |
842 | + got_masterscope_1 = true; |
843 | + } |
844 | + else if (inf.name == "Sample Master Scope 2") |
845 | + { |
846 | + assert (inf.id == "masterscope_b.scope"); |
847 | + assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterB"); |
848 | + assert (inf.dbus_path == "/com/canonical/unity/scope/masterb"); |
849 | + assert (inf.is_master == true); |
850 | + assert (inf.icon == "/usr/share/unity/6/icon2.svg"); |
851 | + assert (inf.keywords.length () == 2); |
852 | + assert (inf.keywords.nth_data (0) == "misc"); |
853 | + assert (inf.keywords.nth_data (1) == "booze"); |
854 | + assert (inf.type == "booze"); |
855 | + assert (inf.query_pattern == "^("); |
856 | + assert (inf.description == "Find even more stuff"); |
857 | + assert (inf.search_hint == "Search things"); |
858 | + |
859 | + check_scope_b_subscopes (node); |
860 | + |
861 | + got_masterscope_2 = true; |
862 | + } |
863 | + else if (inf.name == "Sample Master Scope 3") |
864 | + { |
865 | + assert (inf.id == "masterscope_c.scope"); |
866 | + |
867 | + assert (node.sub_scopes.length () == 1); |
868 | + assert (node.sub_scopes.nth_data (0).id == "test_masterscope.scope"); |
869 | + got_masterscope_3 = true; |
870 | + } |
871 | + } |
872 | + |
873 | + assert (got_masterscope_1 == true); |
874 | + assert (got_masterscope_2 == true); |
875 | + assert (got_masterscope_3 == true); |
876 | + } |
877 | + |
878 | + internal static void test_find_all_scopes_xdg () |
879 | + { |
880 | + // ignore warnings |
881 | + var error_handler = new ErrorHandler (); |
882 | + error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); |
883 | + |
884 | + Protocol.ScopeRegistry reg = null; |
885 | + |
886 | + var ml = new MainLoop (); |
887 | + Protocol.ScopeRegistry.find_scopes.begin (null, (obj, res) => |
888 | + { |
889 | + reg = Protocol.ScopeRegistry.find_scopes.end (res); |
890 | + ml.quit (); |
891 | + }); |
892 | + |
893 | + run_with_timeout (ml, 5000); |
894 | + |
895 | + assert (reg != null); |
896 | + assert (reg.scopes != null); |
897 | + assert (reg.scopes.length () == NUM_TOP_LEVEL_TEST_SCOPES); |
898 | + |
899 | + bool got_masterscope_1 = false; |
900 | + bool got_masterscope_2 = false; |
901 | + bool got_masterscope_3 = false; |
902 | + |
903 | + foreach (var node in reg.scopes) |
904 | + { |
905 | + assert (node != null); |
906 | + var inf = node.scope_info; |
907 | + if (inf.name == "Sample Master Scope 1") |
908 | + { |
909 | + assert (inf.id == "masterscope_a.scope"); |
910 | + assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterA"); |
911 | + assert (inf.dbus_path == "/com/canonical/unity/scope/mastera"); |
912 | + assert (inf.is_master == true); |
913 | + assert (inf.icon == "/usr/share/unity/6/icon1.svg"); |
914 | + assert (inf.required_metadata.columns.length == 3); |
915 | + var col = inf.required_metadata.columns[0]; |
916 | + assert (col.name == "some_id"); |
917 | + assert (col.type_id == "s"); |
918 | + col = inf.required_metadata.columns[1]; |
919 | + assert (col.name == "foo"); |
920 | + assert (col.type_id == "s"); |
921 | + col = inf.required_metadata.columns[2]; |
922 | + assert (col.name == "bar"); |
923 | + assert (col.type_id == "y"); |
924 | + assert (inf.optional_metadata != null); |
925 | + assert (inf.optional_metadata.columns.length == 1); |
926 | + col = inf.optional_metadata.columns[0]; |
927 | + assert (col.name == "baz"); |
928 | + assert (col.type_id == "u"); |
929 | + assert (inf.keywords.length () == 1); |
930 | + assert (inf.type == "varia"); |
931 | + assert (inf.query_pattern == ""); |
932 | + assert (inf.description == "Find various stuff"); |
933 | + assert (inf.search_hint == "Search stuff"); |
934 | + assert (node.sub_scopes == null); |
935 | + |
936 | + got_masterscope_1 = true; |
937 | + } |
938 | + else if (inf.name == "Sample Master Scope 2") |
939 | + { |
940 | + assert (inf.id == "masterscope_b.scope"); |
941 | + assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterB"); |
942 | + assert (inf.dbus_path == "/com/canonical/unity/scope/masterb"); |
943 | + assert (inf.is_master == true); |
944 | + assert (inf.icon == "/usr/share/unity/6/icon2.svg"); |
945 | + assert (inf.keywords.length () == 2); |
946 | + assert (inf.keywords.nth_data (0) == "misc"); |
947 | + assert (inf.keywords.nth_data (1) == "booze"); |
948 | + assert (inf.type == "booze"); |
949 | + assert (inf.query_pattern == "^("); |
950 | + assert (inf.description == "Find even more stuff"); |
951 | + assert (inf.search_hint == "Search things"); |
952 | + |
953 | + check_scope_b_subscopes (node); |
954 | + |
955 | + got_masterscope_2 = true; |
956 | + } |
957 | + else if (inf.name == "Sample Master Scope 3") |
958 | + { |
959 | + assert (inf.id == "masterscope_c.scope"); |
960 | + |
961 | + assert (node.sub_scopes.length () == 1); |
962 | + assert (node.sub_scopes.nth_data (0).id == "test_masterscope.scope"); |
963 | + got_masterscope_3 = true; |
964 | + } |
965 | + } |
966 | + |
967 | + assert (got_masterscope_1 == true); |
968 | + assert (got_masterscope_2 == true); |
969 | + assert (got_masterscope_3 == true); |
970 | + } |
971 | + |
972 | + internal static void test_find_all_scopes_with_hidden_scopes () |
973 | + { |
974 | + var settings = new Settings ("com.canonical.Unity.Lenses"); |
975 | + settings.set_strv ("hidden-scopes", |
976 | + {"masterscope_a.scope", "test_masterscope.scope"}); |
977 | + unity_protocol_scope_registry_scope_metadata_update_hidden_scope_ids (); |
978 | + // ignore warnings |
979 | + var error_handler = new ErrorHandler (); |
980 | + error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); |
981 | + |
982 | + Protocol.ScopeRegistry reg = null; |
983 | + |
984 | + var ml = new MainLoop (); |
985 | + Protocol.ScopeRegistry.find_scopes.begin (null, (obj, res) => |
986 | + { |
987 | + reg = Protocol.ScopeRegistry.find_scopes.end (res); |
988 | + ml.quit (); |
989 | + }); |
990 | + |
991 | + run_with_timeout (ml, 5000); |
992 | + |
993 | + assert (reg != null); |
994 | + assert (reg.scopes != null); |
995 | + // there are exactly 4 top-level scopes (master scopes) - 2 disabled |
996 | + assert (reg.scopes.length () == NUM_TOP_LEVEL_TEST_SCOPES - 2); |
997 | + |
998 | + bool got_masterscope_1 = false; |
999 | + bool got_masterscope_2 = false; |
1000 | + bool got_masterscope_3 = false; |
1001 | + |
1002 | + foreach (var node in reg.scopes) |
1003 | + { |
1004 | + assert (node != null); |
1005 | + var inf = node.scope_info; |
1006 | + if (inf.name == "Sample Master Scope 1") |
1007 | + { |
1008 | + assert (inf.id == "masterscope_a.scope"); |
1009 | + got_masterscope_1 = true; |
1010 | + } |
1011 | + else if (inf.name == "Sample Master Scope 2") |
1012 | + { |
1013 | + assert (inf.id == "masterscope_b.scope"); |
1014 | + assert (inf.dbus_name == "com.canonical.Unity.Scope.MasterB"); |
1015 | + assert (inf.dbus_path == "/com/canonical/unity/scope/masterb"); |
1016 | + assert (inf.is_master == true); |
1017 | + assert (inf.icon == "/usr/share/unity/6/icon2.svg"); |
1018 | + assert (inf.keywords.length () == 2); |
1019 | + assert (inf.keywords.nth_data (0) == "misc"); |
1020 | + assert (inf.keywords.nth_data (1) == "booze"); |
1021 | + assert (inf.type == "booze"); |
1022 | + assert (inf.query_pattern == "^("); |
1023 | + assert (inf.description == "Find even more stuff"); |
1024 | + assert (inf.search_hint == "Search things"); |
1025 | + |
1026 | + check_scope_b_subscopes (node); |
1027 | + |
1028 | + got_masterscope_2 = true; |
1029 | + } |
1030 | + else if (inf.name == "Sample Master Scope 3") |
1031 | + { |
1032 | + assert (inf.id == "masterscope_c.scope"); |
1033 | + |
1034 | + // the child in this one is disabled, there should be 0 subscopes |
1035 | + assert (node.sub_scopes.length () == 0); |
1036 | + got_masterscope_3 = true; |
1037 | + } |
1038 | + } |
1039 | + |
1040 | + assert (got_masterscope_1 == false); |
1041 | + assert (got_masterscope_2 == true); |
1042 | + assert (got_masterscope_3 == true); |
1043 | + |
1044 | + // cleanup |
1045 | + settings.set_strv ("hidden-scopes", {}); |
1046 | + unity_protocol_scope_registry_scope_metadata_update_hidden_scope_ids (); |
1047 | } |
1048 | |
1049 | internal static void test_find_specific_scope () |
1050 | @@ -229,17 +410,20 @@ |
1051 | |
1052 | internal static void test_find_scope_by_id () |
1053 | { |
1054 | - // ignore warnings |
1055 | + // ignore warnings, because there's broken scope in the dir |
1056 | var error_handler = new ErrorHandler (); |
1057 | error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); |
1058 | |
1059 | Protocol.ScopeRegistry reg = null; |
1060 | |
1061 | var ml = new MainLoop (); |
1062 | - Protocol.ScopeRegistry.find_scopes_for_id.begin ("masterscope_b.scope", null, |
1063 | - (obj, res) => |
1064 | + Protocol.ScopeRegistry.find_scopes_for_id.begin ("masterscope_b.scope", null, (obj, res) => |
1065 | { |
1066 | - reg = Protocol.ScopeRegistry.find_scopes_for_id.end (res); |
1067 | + try |
1068 | + { |
1069 | + reg = Protocol.ScopeRegistry.find_scopes_for_id.end (res); |
1070 | + } |
1071 | + catch (Error e) { assert_not_reached (); } |
1072 | ml.quit (); |
1073 | }); |
1074 | |
1075 | @@ -247,12 +431,76 @@ |
1076 | |
1077 | assert (reg != null); |
1078 | assert (reg.scopes != null); |
1079 | - assert (reg.scopes.length () == 2); // there are exactly 2 top-level scopes (sub-scopes of masterscope_b) |
1080 | + assert (reg.scopes.length () == 2); // there are exactly 2 scopes (sub-scopes of masterscope_b) |
1081 | |
1082 | var node = reg.scopes.nth_data (0); |
1083 | assert (node != null); |
1084 | } |
1085 | |
1086 | + internal static void test_find_overridden_scope_by_id () |
1087 | + { |
1088 | + string[] extra_dirs = { |
1089 | + Config.TESTDIR + "/data/unity/customized_scopes", |
1090 | + Config.TESTDIR + "/data/non_existant", |
1091 | + Config.TESTDIR + "/data/unity/scopes", |
1092 | + }; |
1093 | + string dirs = string.joinv (":", extra_dirs); |
1094 | + Environment.set_variable ("LIBUNITY_SCOPE_DIRECTORIES", dirs, true); |
1095 | + unity_protocol_scope_registry_init_scope_directories (); |
1096 | + unity_protocol_scope_registry_init_scope_file_prefixes (); |
1097 | + |
1098 | + // ignore warnings, because there's broken scope in the dir |
1099 | + var error_handler = new ErrorHandler (); |
1100 | + error_handler.ignore_message (PROTO_DOMAIN, LogLevelFlags.LEVEL_WARNING); |
1101 | + |
1102 | + Protocol.ScopeRegistry reg = null; |
1103 | + |
1104 | + var ml = new MainLoop (); |
1105 | + Protocol.ScopeRegistry.find_scopes_for_id.begin ("masterscope_b.scope", null, (obj, res) => |
1106 | + { |
1107 | + try |
1108 | + { |
1109 | + reg = Protocol.ScopeRegistry.find_scopes_for_id.end (res); |
1110 | + } |
1111 | + catch (Error e) { assert_not_reached (); } |
1112 | + ml.quit (); |
1113 | + }); |
1114 | + |
1115 | + run_with_timeout (ml, 5000); |
1116 | + |
1117 | + assert (reg != null); |
1118 | + assert (reg.scopes != null); |
1119 | + assert (reg.scopes.length () == 3); // there are exactly 3 scopes (sub-scopes of masterscope_b) |
1120 | + |
1121 | + bool found_subscope1 = false; |
1122 | + bool found_subscope2 = false; |
1123 | + bool found_override = false; |
1124 | + foreach (var node in reg.scopes) |
1125 | + { |
1126 | + switch (node.scope_info.id) |
1127 | + { |
1128 | + case "masterscope_b-subscope1.scope": found_subscope1 = true; |
1129 | + break; |
1130 | + case "masterscope_b-subscope2.scope": found_subscope2 = true; |
1131 | + break; |
1132 | + case "masterscope_b-subscope-custom.scope": found_override = true; |
1133 | + break; |
1134 | + default: warning ("Found unknown subscope: %s", node.scope_info.id); |
1135 | + break; |
1136 | + } |
1137 | + } |
1138 | + |
1139 | + assert (found_subscope1); |
1140 | + assert (found_subscope2); |
1141 | + assert (found_override); |
1142 | + |
1143 | + // cleanup |
1144 | + Environment.set_variable ("LIBUNITY_SCOPE_DIRECTORIES", |
1145 | + "%s/data/unity/scopes".printf (Config.TESTDIR), true); |
1146 | + unity_protocol_scope_registry_init_scope_directories (); |
1147 | + unity_protocol_scope_registry_init_scope_file_prefixes (); |
1148 | + } |
1149 | + |
1150 | internal static void test_metadata_load_by_scope_id () |
1151 | { |
1152 | var metadata = Protocol.ScopeRegistry.ScopeMetadata.for_id ("masterscope_a.scope"); |
PASSED: Continuous integration, rev:290 jenkins. qa.ubuntu. com/job/ libunity- ci/110/ jenkins. qa.ubuntu. com/job/ libunity- saucy-amd64- ci/95 jenkins. qa.ubuntu. com/job/ libunity- saucy-armhf- ci/95 jenkins. qa.ubuntu. com/job/ libunity- saucy-i386- ci/95
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
SUCCESS: http://
Click here to trigger a rebuild: s-jenkins: 8080/job/ libunity- ci/110/ rebuild
http://