media-export: Disable Artist/All container
[profile/ivi/rygel.git] / tests / rygel-user-config-test.vala
1 /*
2  * Copyright (C) 2012 Intel Corporation
3  *
4  * Author: Krzesimir Nowak <krnowak@openismus.com>
5  *
6  * This file is part of Rygel.
7  *
8  * Rygel is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * Rygel is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  */
22
23 using Gee;
24
25 public class Rygel.UserConfigTest : GLib.Object {
26
27     // pitiful Vala with no typedefs...
28     public class ConfigSet : GLib.Object {
29         public HashSet<ConfigurationEntry> gee;
30
31         public ConfigSet () {
32             this.gee = new HashSet<ConfigurationEntry> ();
33         }
34     }
35
36     public class SectionMap : GLib.Object {
37         public HashMap<string, HashSet<SectionEntry> > gee;
38
39         public SectionMap () {
40             this.gee = new HashMap<string, HashSet<SectionEntry> > ();
41         }
42
43         public HashSet<SectionEntry> new_values (string section) {
44             var values = new HashSet<SectionEntry> ();
45
46             this.gee.set (section, values);
47
48             return values;
49         }
50     }
51
52     public class SettingMap : GLib.Object {
53         public HashMap<string, HashSet<string> > gee;
54
55         public SettingMap () {
56             this.gee = new HashMap<string, HashSet<string> > ();
57         }
58
59         public HashSet<string> new_values (string section) {
60             var values = new HashSet<string> ();
61
62             this.gee.set (section, values);
63
64             return values;
65         }
66     }
67
68     private class Settings : GLib.Object {
69         public string? general_title;
70         public bool? general_enabled;
71         public bool? general_upnp_enabled;
72         public string? general_iface;
73         public int? general_port;
74         public string? foo_title;
75         public bool? foo_enabled;
76         public bool? foo_setting;
77
78         private void initialize (string? general_title = null,
79                                  bool? general_enabled = null,
80                                  bool? general_upnp_enabled = null,
81                                  string? general_iface = null,
82                                  int? general_port = null,
83                                  string? foo_title = null,
84                                  bool? foo_enabled = null,
85                                  bool? foo_setting = null) {
86             this.general_title = general_title;
87             this.general_enabled = general_enabled;
88             this.general_upnp_enabled = general_upnp_enabled;
89             this.general_iface = general_iface;
90             this.general_port = general_port;
91             this.foo_title = foo_title;
92             this.foo_enabled = foo_enabled;
93             this.foo_setting = foo_setting;
94         }
95
96         public Settings (string? general_title = null,
97                          bool? general_enabled = null,
98                          bool? general_upnp_enabled = null,
99                          string? general_iface = null,
100                          int? general_port = null,
101                          string? foo_title = null,
102                          bool? foo_enabled = null,
103                          bool? foo_setting = null) {
104             this.initialize (general_title,
105                              general_enabled,
106                              general_upnp_enabled,
107                              general_iface,
108                              general_port,
109                              foo_title,
110                              foo_enabled,
111                              foo_setting);
112         }
113
114         public Settings.default () {
115             this.initialize ("General",
116                              true,
117                              true,
118                              "eth0",
119                              42,
120                              "Foo",
121                              true,
122                              true);
123         }
124     }
125
126     private abstract class SettingsAction : GLib.Object {
127         protected UserConfigTest test;
128
129         SettingsAction (UserConfigTest test) {
130             this.test = test;
131         }
132         public abstract void perform (string config);
133     }
134
135     private class SettingsDoNothing : SettingsAction {
136         public SettingsDoNothing (UserConfigTest test) {
137             base (test);
138         }
139
140         public override void perform (string config) {}
141     }
142
143     private class SettingsReplace : SettingsAction {
144         private Settings settings;
145
146         public SettingsReplace (UserConfigTest test,
147                                 Settings settings) {
148             base (test);
149             this.settings = settings;
150         }
151
152         public override void perform (string config) {
153             try {
154                 this.test.set_config (config,
155                                   this.settings);
156             } catch (GLib.Error error) {
157                 assert_not_reached ();
158             }
159         }
160     }
161
162     private class SettingsRemove : SettingsAction {
163         public SettingsRemove (UserConfigTest test) {
164             base (test);
165         }
166
167         public override void perform (string config) {
168             this.test.remove_config (config);
169         }
170     }
171
172     private class WatchData : GLib.Object {
173         public string description;
174         public SettingsAction local_action;
175         public SettingsAction system_action;
176         public ConfigSet expected_config_changes;
177         public SectionMap expected_section_changes;
178         public SettingMap expected_setting_changes;
179
180         private bool description_printed;
181
182         private void initialize (string description,
183                                  SettingsAction local_action,
184                                  SettingsAction system_action,
185                                  ConfigSet expected_config_changes,
186                                  SectionMap expected_section_changes,
187                                  SettingMap expected_setting_changes) {
188             this.description = description;
189             this.local_action = local_action;
190             this.system_action = system_action;
191             this.expected_config_changes = expected_config_changes;
192             this.expected_section_changes = expected_section_changes;
193             this.expected_setting_changes = expected_setting_changes;
194             this.description_printed = false;
195         }
196
197         public WatchData (string description,
198                           SettingsAction local_action,
199                           SettingsAction system_action,
200                           ConfigSet expected_config_changes,
201                           SectionMap expected_section_changes,
202                           SettingMap expected_setting_changes) {
203             this.initialize (description,
204                              local_action,
205                              system_action,
206                              expected_config_changes,
207                              expected_section_changes,
208                              expected_setting_changes);
209         }
210
211         public WatchData.no_changes (string description,
212                                      SettingsAction local_action,
213                                      SettingsAction system_action) {
214             this.initialize (description,
215                              local_action,
216                              system_action,
217                              new ConfigSet (),
218                              new SectionMap (),
219                              new SettingMap ());
220         }
221
222         public bool empty () {
223             return (this.expected_config_changes.gee.size == 0 &&
224                     this.expected_section_changes.gee.size == 0 &&
225                     this.expected_setting_changes.gee.size == 0);
226         }
227
228         public void prepare_setup () {
229             this.local_action.perform (LOCAL_CONFIG);
230             this.system_action.perform (SYSTEM_CONFIG);
231         }
232
233         public void print_description () {
234             if (!this.description_printed) {
235                 this.description_printed = true;
236                 warning ("Test case: %s.", this.description);
237             }
238         }
239
240         public void print_expectations () {
241             warning ("Expected configuration changes so far:");
242             if (this.expected_config_changes.gee.size == 0) {
243                 warning ("(none)");
244             } else {
245                 foreach (var entry in this.expected_config_changes.gee) {
246                     warning ("  %s", entry.to_string ());
247                 }
248             }
249             warning ("Expected section changes so far:");
250             if (this.expected_section_changes.gee.size == 0) {
251                 warning ("(none)");
252             } else {
253                 var changes = this.expected_section_changes.gee;
254
255                 foreach (var section in changes.keys) {
256                     var entries = changes.get (section);
257
258                     warning ("  %s", section);
259                     foreach (var entry in entries) {
260                         warning ("    %s", entry.to_string ());
261                     }
262                 }
263             }
264             warning ("Expected setting changes so far:");
265             if (this.expected_setting_changes.gee.size == 0) {
266                 warning ("(none)");
267             } else {
268                 var changes = this.expected_setting_changes.gee;
269
270                 foreach (var section in changes.keys) {
271                     var keys = changes.get (section);
272
273                     warning ("  %s", section);
274                     foreach (var key in keys) {
275                         warning ("    %s", key);
276                     }
277                 }
278             }
279         }
280     }
281
282     private static string LOCAL_CONFIG = "user-config-test-local.ini";
283     private static string SYSTEM_CONFIG = "user-config-test-system.ini";
284     private static string GENERAL = "general";
285     private static string FOO = "foo";
286
287     private MainLoop main_loop;
288     private UserConfig config;
289     private bool fail;
290     private WatchData current_watch_data;
291     private uint timeout_id;
292     private HashMap<string, Settings> last_settings;
293
294     private void set_config (string path,
295                              Settings settings = new Settings ()) throws Error {
296         KeyFile key_file = new KeyFile ();
297
298         this.last_settings.set (path, settings);
299
300         if (settings.general_title != null) {
301             key_file.set_string (GENERAL, "title", settings.general_title);
302         }
303         if (settings.general_enabled != null) {
304             key_file.set_boolean (GENERAL, "enabled", settings.general_enabled);
305         }
306         if (settings.general_upnp_enabled != null) {
307             key_file.set_boolean (GENERAL,
308                                   "upnp-enabled",
309                                   settings.general_upnp_enabled);
310         }
311         if (settings.general_iface != null) {
312             key_file.set_string (GENERAL, "interface", settings.general_iface);
313         }
314         if (settings.general_port != null) {
315             key_file.set_integer (GENERAL, "port", settings.general_port);
316         }
317
318         if (settings.foo_title != null) {
319             key_file.set_string (FOO, "title", settings.foo_title);
320         }
321         if (settings.foo_enabled != null) {
322             key_file.set_boolean (FOO, "enabled", settings.foo_enabled);
323         }
324         if (settings.foo_setting != null) {
325             key_file.set_boolean (FOO, "setting", settings.foo_setting);
326         }
327
328         var tmp_path = path + ".tmp";
329         size_t size;
330         var data = key_file.to_data (out size);
331
332         FileUtils.set_contents (tmp_path, data, (ssize_t)size);
333         FileUtils.rename (tmp_path, path);
334     }
335
336     private void remove_config (string path) {
337         FileUtils.unlink (path);
338         this.last_settings.set (path, new Settings ());
339     }
340
341     public UserConfigTest () {
342         this.main_loop = new MainLoop (null, false);
343         this.fail = false;
344         this.timeout_id = 0;
345         this.last_settings = new HashMap<string, Settings> ();
346
347         this.last_settings.set (LOCAL_CONFIG, new Settings ());
348         this.last_settings.set (SYSTEM_CONFIG, new Settings ());
349     }
350
351     private void try_load (bool expect_failure) {
352         var failed = false;
353
354         try {
355             var config = new UserConfig.with_paths (LOCAL_CONFIG,
356                                                     SYSTEM_CONFIG);
357             assert (config != null);
358         } catch (Error e) {
359             failed = true;
360         }
361         if (expect_failure != failed) {
362             warning ("Unexpected %s of UserConfig creation.",
363                      (expect_failure ? "success" : "failure"));
364             this.fail = true;
365         }
366     }
367
368     private class ConfigRemover {
369         private UserConfigTest test;
370
371         public ConfigRemover (UserConfigTest test) {
372             this.test = test;
373         }
374
375         ~ConfigRemover () {
376             this.test.remove_config (LOCAL_CONFIG);
377             this.test.remove_config (SYSTEM_CONFIG);
378         }
379     }
380
381     private void test_loading () {
382         var remover = new ConfigRemover (this);
383         assert (remover != null);
384
385         try {
386             this.set_config (LOCAL_CONFIG);
387         } catch (GLib.Error error) {
388             assert_not_reached ();
389         }
390
391         try {
392             this.set_config (SYSTEM_CONFIG);
393         } catch (GLib.Error error) {
394             assert_not_reached ();
395         }
396         this.try_load (false);
397         this.remove_config (LOCAL_CONFIG);
398         this.try_load (false);
399         this.remove_config (SYSTEM_CONFIG);
400         this.try_load (true);
401         // Should not fail when system config does not exist but local
402         // do.
403         // https://bugzilla.gnome.org/show_bug.cgi?id=683959
404
405         // this.set_config (LOCAL_CONFIG);
406         // this.try_load (false);
407     }
408
409     private void data_check () {
410         if (this.current_watch_data.empty ()) {
411             if (this.timeout_id != 0) {
412                 Source.remove (this.timeout_id);
413                 this.timeout_id = 0;
414             }
415             this.main_loop.quit ();
416         }
417     }
418
419     private void on_configuration_changed (Configuration config,
420                                            ConfigurationEntry entry) {
421         var changes = this.current_watch_data.expected_config_changes.gee;
422
423         if (changes.remove (entry)) {
424             this.data_check ();
425         } else {
426             this.current_watch_data.print_description ();
427             warning ("Unexpected change of configuration entry: %s",
428                      entry.to_string ());
429             this.fail = true;
430         }
431     }
432
433     private void on_section_changed (Configuration config,
434                                      string section,
435                                      SectionEntry entry) {
436         var changes = this.current_watch_data.expected_section_changes.gee;
437
438         if (changes.has_key (section)) {
439             var entries = changes.get (section);
440
441             if (entries.remove (entry)) {
442                 if (entries.size == 0) {
443                     changes.unset (section);
444                 }
445                 this.data_check ();
446             } else {
447                 this.current_watch_data.print_description ();
448                 warning ("Unexpected change in expected section: %s, " +
449                          "unexpected entry: %s.",
450                          section,
451                          entry.to_string ());
452                 this.fail = true;
453             }
454         } else {
455             this.current_watch_data.print_description ();
456             warning ("Unexpected change in unexpected section: %s, entry %s.",
457                      section,
458                      entry.to_string ());
459             this.fail = true;
460         }
461     }
462
463     private void on_setting_changed (Configuration config,
464                                      string section,
465                                      string key) {
466         var changes = this.current_watch_data.expected_setting_changes.gee;
467
468         if (changes.has_key (section)) {
469             var keys = changes.get (section);
470
471             if (keys.remove (key)) {
472                 if (keys.size == 0) {
473                     changes.unset (section);
474                 }
475                 this.data_check ();
476             } else {
477                 this.current_watch_data.print_description ();
478                 warning ("Unexpected change in expected setting section: %s, " +
479                          "unexpected setting key: %s.",
480                          section,
481                          key);
482                 this.fail = true;
483             }
484         } else {
485             this.current_watch_data.print_description ();
486             warning ("Unexpected change in unexpected setting section: %s, " +
487                      "setting key: %s",
488                      section,
489                      key);
490             this.fail = true;
491         }
492     }
493
494     private ArrayList<WatchData> prepare_watch_data () {
495         var data = new ArrayList<WatchData> ();
496         var do_nothing = new SettingsDoNothing (this);
497
498
499         // change nothing, expect no changes.
500         {
501             var desc = "change nothing, expect no changes";
502
503             data.add (new WatchData.no_changes (desc, do_nothing, do_nothing));
504         }
505
506         // set new config but with the same contents as before, expect
507         // no changes.
508         {
509             var desc = "set new config but with the same contents as before, " +
510                 "expect no changes";
511             var last_local = new SettingsReplace
512                                         (this,
513                                          this.last_settings.get (LOCAL_CONFIG));
514             var last_system = new SettingsReplace
515                                        (this,
516                                         this.last_settings.get (SYSTEM_CONFIG));
517
518             data.add (new WatchData.no_changes (desc, last_local, last_system));
519         }
520
521         // set empty system config, expect no changes
522         {
523             var desc = "set empty system config, expect no changes";
524             var empty = new SettingsReplace (this, new Settings ());
525
526             data.add (new WatchData.no_changes (desc, do_nothing, empty));
527         }
528
529         // change all possible values in local config, expect lots of
530         // changes
531         {
532             var desc = "change all possible values in local config, expect " +
533                 "lots of changes";
534             var config = new ConfigSet ();
535
536             config.gee.add (ConfigurationEntry.UPNP_ENABLED);
537             config.gee.add (ConfigurationEntry.INTERFACE);
538             config.gee.add (ConfigurationEntry.PORT);
539
540             var section = new SectionMap ();
541             var general_section = section.new_values (GENERAL);
542             var foo_section = section.new_values (FOO);
543
544             general_section.add (SectionEntry.TITLE);
545             general_section.add (SectionEntry.ENABLED);
546             foo_section.add (SectionEntry.TITLE);
547             foo_section.add (SectionEntry.ENABLED);
548
549             var setting = new SettingMap ();
550             var foo_setting = setting.new_values (FOO);
551
552             foo_setting.add ("setting");
553
554             var new_local = new SettingsReplace
555                                         (this,
556                                          new Settings ("Changed!",
557                                                        false,
558                                                        false,
559                                                        "Changed!",
560                                                        13,
561                                                        "Changed!",
562                                                        false,
563                                                        false));
564
565             data.add (new WatchData (desc,
566                                      new_local,
567                                      do_nothing,
568                                      config,
569                                      section,
570                                      setting));
571         }
572
573         // add system config back, expect no changes
574         {
575             var desc = "add system config back, expect no changes";
576             var system_default = new SettingsReplace (this,
577                                                       new Settings.default ());
578
579             data.add (new WatchData.no_changes (desc,
580                                                 do_nothing,
581                                                 system_default));
582         }
583
584         // remove several keys from local config, expect changes for those
585         {
586             var desc = "remove several keys from local config, expect changes" +
587                 " for those";
588             var config = new ConfigSet ();
589
590             config.gee.add (ConfigurationEntry.INTERFACE);
591
592             var section = new SectionMap ();
593             var general_section = section.new_values (GENERAL);
594             var foo_section = section.new_values (FOO);
595
596             general_section.add (SectionEntry.TITLE);
597             foo_section.add (SectionEntry.ENABLED);
598
599             var setting = new SettingMap ();
600             var foo_setting = setting.new_values (FOO);
601
602             foo_setting.add ("setting");
603
604             var new_local = new SettingsReplace
605                                         (this,
606                                          new Settings (null,
607                                                        false,
608                                                        false,
609                                                        null,
610                                                        13,
611                                                        "Changed!",
612                                                        null,
613                                                        null));
614
615             data.add (new WatchData (desc,
616                                      new_local,
617                                      do_nothing,
618                                      config,
619                                      section,
620                                      setting));
621         }
622
623         // remove local config, expect changes for the rest of settings
624         {
625             var desc = "remove local config, expect changes for the rest of " +
626                 "settings";
627             var config = new ConfigSet ();
628
629             config.gee.add (ConfigurationEntry.UPNP_ENABLED);
630             config.gee.add (ConfigurationEntry.PORT);
631
632             var section = new SectionMap ();
633             var general_section = section.new_values (GENERAL);
634             var foo_section = section.new_values (FOO);
635
636             general_section.add (SectionEntry.ENABLED);
637             foo_section.add (SectionEntry.TITLE);
638
639             var setting = new SettingMap ();
640
641             data.add (new WatchData (desc,
642                                      new SettingsRemove (this),
643                                      do_nothing,
644                                      config,
645                                      section,
646                                      setting));
647         }
648
649         return data;
650     }
651
652     private void test_watching () {
653         var remover = new ConfigRemover (this);
654         assert (remover != null);
655         var full_settings = new Settings.default ();
656         assert (full_settings != null);
657
658         try {  
659             this.set_config (LOCAL_CONFIG,
660                              full_settings);
661         } catch (GLib.Error error) {
662             assert_not_reached ();
663         }
664
665         try {
666             this.set_config (SYSTEM_CONFIG,
667                              full_settings);
668         } catch (GLib.Error error) {
669             assert_not_reached ();
670         }
671
672         try {
673             this.config = new UserConfig.with_paths (LOCAL_CONFIG, SYSTEM_CONFIG);
674         } catch (GLib.Error error) {
675             assert_not_reached ();
676         }
677
678         assert (this.config != null);
679         this.config.configuration_changed.connect
680                                         (this.on_configuration_changed);
681         this.config.section_changed.connect (this.on_section_changed);
682         this.config.setting_changed.connect (this.on_setting_changed);
683
684         // this have to be after setting local and system config
685         var watch_data_array = this.prepare_watch_data ();
686
687         foreach (var watch_data in watch_data_array) {
688             this.current_watch_data = watch_data;
689
690             this.timeout_id = Timeout.add_seconds (2, () => {
691                 if (!this.current_watch_data.empty ()) {
692                     this.current_watch_data.print_description ();
693                     warning ("Test timed out and not all expected changes " +
694                              "happened.");
695                     this.current_watch_data.print_expectations ();
696                     this.fail = true;
697                 }
698                 this.timeout_id = 0;
699                 this.main_loop.quit ();
700                 return false;
701             });
702
703             watch_data.prepare_setup ();
704             this.main_loop.run ();
705             if (this.fail) {
706                 return;
707             }
708         }
709     }
710
711     public int run () throws Error {
712         test_loading ();
713         test_watching ();
714
715         if (this.fail) {
716             return 1;
717         }
718         return 0;
719     }
720
721     public static int main (string[] args) {
722         var test = new UserConfigTest ();
723
724         try {
725             return test.run ();
726         } catch (Error e) {
727             return 1;
728         }
729     }
730 }