Merge lp:~mhr3/libunity/remove-scopes-via-dconf into lp:libunity

Proposed by Michal Hruby
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
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.

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
291. By Michal Hruby

Add a test for the XDG branch of find_scopes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
292. By Michal Hruby

Add tests for hidden scopes

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
293. By Michal Hruby

Bump changelog

294. By Michal Hruby

Skip non existing directories without throwing an error

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
295. By Michal Hruby

Finish up merging from multiple locations

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 :

27 + <key type="as" name="locked-scopes">
Why is this part of this MP?

100 + var overriden_dirs = Environment.get_variable ("LIBUNITY_SCOPE_DIRECTORIES");
I think just SCOPE_DIRECTORIES would be fine?

169 + if (scope_id in hidden_scope_ids)
170 + throw new ParseError.FILE_NOT_FOUND ("Scope not found: %s", scope_id);

196 + if (data.id in hidden_scope_ids)
197 + throw new ParseError.FILE_NOT_FOUND ("Scope not found: %s", data.id);

I think we shouldn't be hiding the real cause and instead extend ParseError domain with SCOPE_HIDDEN or so.

265 + yield build_scope_node_tree (root_node, scope_dir);
In theory build_scope_node_tree may throw (in practice it won't for 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_node.data.get_overrides_subscopes () &&
440 + child_node.n_children () == 0)
...
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?

review: Needs Fixing
296. By Michal Hruby

Fixes based on review comments

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

> 27 + <key type="as" name="locked-scopes">
> Why is this part of this MP?

Both removing scopes via dconf and locking them are features for OEMs.

> 100 + var overriden_dirs = Environment.get_variable
> ("LIBUNITY_SCOPE_DIRECTORIES");
> 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_TIME_SEARCHES etc.)

> 169 + if (scope_id in hidden_scope_ids)
> 170 + throw new ParseError.FILE_NOT_FOUND ("Scope not found: %s",
> scope_id);
>
> 196 + if (data.id in hidden_scope_ids)
> 197 + throw new ParseError.FILE_NOT_FOUND ("Scope not found: %s",
> 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.HIDDEN_SCOPE) { ... // haha, you're lying! I'll do something about it }"

> 265 + yield build_scope_node_tree (root_node, scope_dir);
> In theory build_scope_node_tree may throw (in practice it won't for
> 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_node.data.get_overrides_subscopes () &&
> 440 + child_node.n_children () == 0)
> ...
> 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.

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 :

> > 27 + <key type="as" name="locked-scopes">
> > 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.HIDDEN_SCOPE) { ... // haha, you're lying! I'll do something
> about it }"
How about changing error message then? That will be useful when looking at logs.

review: Needs Fixing
Revision history for this message
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

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 :

Looks good, thanks!

review: Approve

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-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");

Subscribers

People subscribed via source and target branches