• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import grp
2import mock_flimflam
3import os
4import pwd
5import stat
6import time
7import utils
8
9from autotest_lib.client.bin import test
10from autotest_lib.client.common_lib import error
11
12class network_ShillInitScripts(test.test):
13    """ Test that shill init scripts perform as expected.  Use the
14        real filesystem (doing a best effort to archive and restore
15        current state).  The shill manager is stopped and a proxy
16        DBus entity is installed to accept DBus messages that are sent
17        via "dbus-send" in the shill startup scripts.  However, the
18        "real" shill is still also started from time to time and we
19        check that it is run with the right command line arguments.
20    """
21    version = 1
22    save_directories = [ '/var/cache/shill',
23                         '/var/cache/flimflam',
24                         '/var/run/shill',
25                         '/var/run/state/logged-in',
26                         '/var/run/dhcpcd',
27                         '/var/lib/dhcpcd',
28                         '/home/chronos/.disable_shill' ]
29    fake_user = 'not-a-real-user@chromium.org'
30    saved_config = '/tmp/network_ShillInitScripts_saved_config.tgz'
31    cryptohome_path_command = 'cryptohome-path'
32    guest_shill_user_profile_dir = '/var/run/shill/guest_user_profile/shill'
33    guest_shill_user_log_dir = '/var/run/shill/guest_user_profile/shill_logs'
34    magic_header = '# --- shill init file test magic header ---'
35
36
37    def start_shill(self):
38        """ Starts a shill instance. """
39        utils.start_service('shill')
40
41
42    def stop_shill(self):
43        """ Halt the running shill instance. """
44        utils.stop_service('shill', ignore_status=True)
45
46        for attempt in range(10):
47            if not self.find_pid('shill'):
48                break
49            time.sleep(1)
50        else:
51            raise error.TestFail('Shill process does not appear to be dying')
52
53
54    def login(self, user=None):
55        """ Simulate the login process.
56
57        Note: "start" blocks until the "script" block completes.
58
59        @param user string user name (email address) to log in.
60
61        """
62
63        if utils.has_systemd():
64            start_cmd = (('systemctl set-environment CHROMEOS_USER=%s'
65                          ' && systemctl start shill-start-user-session') %
66                         (user or self.fake_user))
67        else:
68            start_cmd = ('start shill-start-user-session CHROMEOS_USER=%s' %
69                         (user or self.fake_user))
70        utils.system(start_cmd)
71
72
73    def login_guest(self):
74        """ Simulate guest login.
75
76        For guest login, session-manager passes an empty CHROMEOS_USER arg.
77
78        """
79        self.login('""')
80
81
82    def logout(self):
83        """ Simulate user logout.
84
85        Note: "start" blocks until the "script" block completes.
86
87        """
88        utils.start_service('shill-stop-user-session')
89
90
91    def start_test(self):
92        """ Setup the start of the test.  Stop shill and create test harness."""
93        # Stop a system process on test duts for keeping connectivity up.
94        ret = utils.stop_service('recover_duts', ignore_status=True)
95        self.recover_duts_stopped = (ret == 0);
96
97        self.stop_shill()
98
99        # Deduce the root cryptohome directory name for our fake user.
100        self.root_cryptohome_dir = utils.system_output(
101            '%s system %s' % (self.cryptohome_path_command, self.fake_user))
102
103        # Deduce the user cryptohome directory name for our fake user.
104        self.user_cryptohome_dir = utils.system_output(
105            '%s user %s' % (self.cryptohome_path_command, self.fake_user))
106
107        # Deduce the directory for memory log storage.
108        self.user_cryptohome_log_dir = ('%s/shill_logs' %
109                                        self.root_cryptohome_dir)
110
111        # The sanitized hash of the username is the basename of the cryptohome.
112        self.fake_user_hash = os.path.basename(self.root_cryptohome_dir)
113
114        # Just in case this hash actually exists, add these to the list of
115        # saved directories.
116        self.save_directories.append(self.root_cryptohome_dir)
117        self.save_directories.append(self.user_cryptohome_dir)
118
119        # Archive the system state we will be modifying, then remove them.
120        utils.system('tar zcvf %s --directory / --ignore-failed-read %s'
121                     ' 2>/dev/null' %
122                     (self.saved_config, ' '.join(self.save_directories)))
123        utils.system('rm -rf %s' % ' '.join(self.save_directories),
124                     ignore_status=True)
125
126        # Create the fake user's system cryptohome directory.
127        os.mkdir(self.root_cryptohome_dir)
128        self.new_shill_user_profile_dir = ('%s/shill' %
129                                           self.root_cryptohome_dir)
130        self.new_shill_user_profile = ('%s/shill.profile' %
131                                       self.new_shill_user_profile_dir)
132
133        # Create the fake user's user cryptohome directory.
134        os.mkdir(self.user_cryptohome_dir)
135        self.flimflam_user_profile_dir = ('%s/flimflam' %
136                                          self.user_cryptohome_dir)
137        self.flimflam_user_profile = ('%s/flimflam.profile' %
138                                      self.flimflam_user_profile_dir)
139        self.old_shill_user_profile_dir = ('%s/shill' %
140                                           self.user_cryptohome_dir)
141        self.old_shill_user_profile = ('%s/shill.profile' %
142                                       self.old_shill_user_profile_dir)
143        self.mock_flimflam = None
144
145
146    def start_mock_flimflam(self):
147        """ Start a mock flimflam instance to accept and log DBus calls. """
148        self.mock_flimflam = mock_flimflam.MockFlimflam()
149        self.mock_flimflam.start()
150
151
152    def erase_state(self):
153        """ Remove all the test harness files. """
154        utils.system('rm -rf %s' % ' '.join(self.save_directories))
155        os.mkdir(self.root_cryptohome_dir)
156        os.mkdir(self.user_cryptohome_dir)
157
158
159    def end_test(self):
160        """ Perform cleanup at the end of the test. """
161        if self.mock_flimflam:
162            self.mock_flimflam.quit()
163            self.mock_flimflam.join()
164        self.erase_state()
165        utils.system('tar zxvf %s --directory /' % self.saved_config)
166        utils.system('rm -f %s' % self.saved_config)
167        self.restart_system_processes()
168
169
170    def restart_system_processes(self):
171        """ Restart vital system services at the end of the test. """
172        utils.start_service('shill', ignore_status=True)
173        if self.recover_duts_stopped:
174            utils.start_service('recover_duts', ignore_status=True)
175
176
177    def assure(self, must_be_true, assertion_name):
178        """ Perform a named assertion.
179
180        @param must_be_true boolean parameter that must be true.
181        @param assertion_name string name of this assertion.
182
183        """
184        if not must_be_true:
185            raise error.TestFail('%s: Assertion failed: %s' %
186                                 (self.test_name, assertion_name))
187
188
189    def assure_path_owner(self, path, owner):
190        """ Assert that |path| is owned by |owner|.
191
192        @param path string pathname to test.
193        @param owner string user name that should own |path|.
194
195        """
196        self.assure(pwd.getpwuid(os.stat(path).st_uid)[0] == owner,
197                    'Path %s is owned by %s' % (path, owner))
198
199
200    def assure_path_group(self, path, group):
201        """ Assert that |path| is owned by |group|.
202
203        @param path string pathname to test.
204        @param group string group name that should own |path|.
205
206        """
207        self.assure(grp.getgrgid(os.stat(path).st_gid)[0] == group,
208                    'Path %s is group-owned by %s' % (path, group))
209
210
211    def assure_exists(self, path, path_friendly_name):
212        """ Assert that |path| exists.
213
214        @param path string pathname to test.
215        @param path_friendly_name string user-parsable description of |path|.
216
217        """
218        self.assure(os.path.exists(path), '%s exists' % path_friendly_name)
219
220
221    def assure_is_dir(self, path, path_friendly_name):
222        """ Assert that |path| is a directory.
223
224        @param path string pathname to test.
225        @param path_friendly_name string user-parsable description of |path|.
226
227        """
228        self.assure_exists(path, path_friendly_name)
229        self.assure(stat.S_ISDIR(os.lstat(path).st_mode),
230                    '%s is a directory' % path_friendly_name)
231
232
233    def assure_is_link(self, path, path_friendly_name):
234        """ Assert that |path| is a symbolic link.
235
236        @param path string pathname to test.
237        @param path_friendly_name string user-parsable description of |path|.
238
239        """
240        self.assure_exists(path, path_friendly_name)
241        self.assure(stat.S_ISLNK(os.lstat(path).st_mode),
242                    '%s is a symbolic link' % path_friendly_name)
243
244
245    def assure_is_link_to(self, path, pointee, path_friendly_name):
246        """ Assert that |path| is a symbolic link to |pointee|.
247
248        @param path string pathname to test.
249        @param pointee string pathname that |path| should point to.
250        @param path_friendly_name string user-parsable description of |path|.
251
252        """
253        self.assure_is_link(path, path_friendly_name)
254        self.assure(os.readlink(path) == pointee,
255                    '%s is a symbolic link to %s' %
256                    (path_friendly_name, pointee))
257
258
259    def assure_method_calls(self, expected_method_calls, assertion_name):
260        """ Assert that |expected_method_calls| were executed on mock_flimflam.
261
262        @param expected_method_calls list of string-tuple pairs of method
263            name + tuple of arguments.
264        @param assertion_name string name to assign to the assertion.
265
266        """
267        method_calls = self.mock_flimflam.get_method_calls()
268        if len(expected_method_calls) != len(method_calls):
269            self.assure(False, '%s: method call count does not match' %
270                        assertion_name)
271        for expected, actual in zip(expected_method_calls, method_calls):
272            self.assure(actual.method == expected[0],
273                        '%s: method %s matches expected %s' %
274                        (assertion_name, actual.method, expected[0]))
275            self.assure(actual.argument == expected[1],
276                        '%s: argument %s matches expected %s' %
277                        (assertion_name, actual.argument, expected[1]))
278
279
280    def create_file_with_contents(self, filename, contents):
281        """ Create a file named |filename| that contains |contents|.
282
283        @param filename string name of file.
284        @param contents string contents of file.
285
286        """
287        with open(filename, 'w') as f:
288            f.write(contents)
289
290
291    def touch(self, filename):
292        """ Create an empty file named |filename|.
293
294        @param filename string name of file.
295
296        """
297        self.create_file_with_contents(filename, '')
298
299
300    def create_new_shill_user_profile(self, contents):
301        """ Create a fake new user profile with |contents|.
302
303        @param contents string contents of the new user profile.
304
305        """
306        os.mkdir(self.new_shill_user_profile_dir)
307        self.create_file_with_contents(self.new_shill_user_profile, contents)
308
309
310    def create_old_shill_user_profile(self, contents):
311        """ Create a fake old-style user profile with |contents|.
312
313        @param contents string contents of the old user profile.
314
315        """
316        os.mkdir(self.old_shill_user_profile_dir)
317        self.create_file_with_contents(self.old_shill_user_profile, contents)
318
319
320    def create_flimflam_user_profile(self, contents):
321        """ Create a legacy flimflam user profile with |contents|.
322
323        @param contents string contents of the flimflam user profile.
324
325        """
326        os.mkdir(self.flimflam_user_profile_dir)
327        self.create_file_with_contents(self.flimflam_user_profile, contents)
328
329
330    def file_contents(self, filename):
331        """ Returns the contents of |filename|.
332
333        @param filename string name of file to read.
334
335        """
336        with open(filename) as f:
337            return f.read()
338
339
340    def find_pid(self, process_name):
341        """ Returns the process id of |process_name|.
342
343        @param process_name string name of process to search for.
344
345        """
346        return utils.system_output('pgrep %s' % process_name,
347                                   ignore_status=True).split('\n')[0]
348
349
350    def get_commandline(self):
351        """ Returns the command line of the current shill executable. """
352        pid = self.find_pid('shill')
353        return file('/proc/%s/cmdline' % pid).read().split('\0')
354
355
356    def run_once(self):
357        """ Main test loop. """
358        try:
359            self.start_test()
360        except:
361            self.restart_system_processes()
362            raise
363
364        try:
365            self.run_tests([
366                self.test_start_shill,
367                self.test_start_logged_in,
368                self.test_start_port_flimflam_profile])
369
370            # The tests above run a real instance of shill, whereas the tests
371            # below rely on a mock instance of shill.  We must take care not
372            # to run the mock at the same time as a real shill instance.
373            self.start_mock_flimflam()
374
375            self.run_tests([
376                self.test_login,
377                self.test_login_guest,
378                self.test_login_profile_exists,
379                self.test_login_old_shill_profile,
380                self.test_login_invalid_old_shill_profile,
381                self.test_login_ignore_old_shill_profile,
382                self.test_login_flimflam_profile,
383                self.test_login_ignore_flimflam_profile,
384                self.test_login_prefer_old_shill_profile,
385                self.test_login_multi_profile,
386                self.test_logout])
387        finally:
388            # Stop any shill instances started during testing.
389            self.stop_shill()
390            self.end_test()
391
392
393    def run_tests(self, tests):
394        """ Executes each of the test subparts in sequence.
395
396        @param tests list of methods to run.
397
398        """
399        for test in tests:
400          self.test_name = test.__name__
401          test()
402          self.stop_shill()
403          self.erase_state()
404
405
406    def test_start_shill(self):
407        """ Test all created pathnames during shill startup.
408
409        Also ensure the push argument is not provided by default.
410
411        """
412        self.touch('/home/chronos/.disable_shill')
413        self.start_shill()
414        self.assure_is_dir('/var/run/shill', 'Shill run directory')
415        self.assure_is_dir('/var/lib/dhcpcd', 'dhcpcd lib directory')
416        self.assure_path_owner('/var/lib/dhcpcd', 'dhcp')
417        self.assure_path_group('/var/lib/dhcpcd', 'dhcp')
418        self.assure_is_dir('/var/run/dhcpcd', 'dhcpcd run directory')
419        self.assure_path_owner('/var/run/dhcpcd', 'dhcp')
420        self.assure_path_group('/var/run/dhcpcd', 'dhcp')
421        self.assure(not os.path.exists('/home/chronos/.disable_shill'),
422                    'Shill disable file does not exist')
423        self.assure('--push=~chronos/shill' not in self.get_commandline(),
424                    'Shill command line does not contain push argument')
425
426
427    def test_start_logged_in(self):
428        """ Tests starting up shill while a user is already logged in.
429
430        The "--push" argument should not be added even though shill is started
431        while a user is logged in.
432
433        """
434        os.mkdir('/var/run/shill')
435        os.mkdir('/var/run/shill/user_profiles')
436        self.create_new_shill_user_profile('')
437        os.symlink(self.new_shill_user_profile_dir,
438                   '/var/run/shill/user_profiles/chronos')
439        self.touch('/var/run/state/logged-in')
440        self.start_shill()
441        self.assure('--push=~chronos/shill' not in self.get_commandline(),
442                    'Shill command line does not contain push argument')
443        os.unlink('/var/run/state/logged-in')
444
445
446    def test_start_port_flimflam_profile(self):
447        """ Test that we can port a flimflam profile to a new shill profile.
448
449        Startup should move an old flimflam profile into place if a shill
450        profile does not already exist.
451
452        """
453        os.mkdir('/var/cache/flimflam')
454        flimflam_profile = '/var/cache/flimflam/default.profile'
455        self.create_file_with_contents(flimflam_profile, self.magic_header)
456        shill_profile = '/var/cache/shill/default.profile'
457        self.start_shill()
458        self.assure(not os.path.exists(flimflam_profile),
459                    'Flimflam profile no longer exists')
460        self.assure(os.path.exists(shill_profile),
461                    'Shill profile exists')
462        self.assure(self.magic_header in self.file_contents(shill_profile),
463                    'Shill default profile contains our magic header')
464
465
466    def test_start_ignore_flimflam_profile(self):
467        """ Test that we ignore a flimflam profile if a new profile exists.
468
469        Startup should ignore an old flimflam profile if a shill profile
470        already exists.
471
472        """
473        os.mkdir('/var/cache/flimflam')
474        os.mkdir('/var/cache/shill')
475        flimflam_profile = '/var/cache/flimflam/default.profile'
476        self.create_file_with_contents(flimflam_profile, self.magic_header)
477        shill_profile = '/var/cache/shill/default.profile'
478        self.touch(shill_profile)
479        self.start_shill()
480        self.assure(os.path.exists(flimflam_profile),
481                    'Flimflam profile still exists')
482        self.assure(self.magic_header not in self.file_contents(shill_profile),
483                    'Shill default profile does not contain our magic header')
484
485
486    def test_login(self):
487        """ Test the login process.
488
489        Login should create a profile directory, then create and push
490        a user profile, given no previous state.
491
492        """
493        os.mkdir('/var/run/shill')
494        self.login()
495        self.assure(not os.path.exists(self.flimflam_user_profile),
496                    'Flimflam user profile does not exist')
497        self.assure(not os.path.exists(self.old_shill_user_profile),
498                    'Old shill user profile does not exist')
499        self.assure(not os.path.exists(self.new_shill_user_profile),
500                    'New shill user profile does not exist')
501        # The DBus "CreateProfile" method should have been handled
502        # by our mock_flimflam instance, so the profile directory
503        # should not have actually been created.
504        self.assure_is_dir(self.new_shill_user_profile_dir,
505                           'New shill user profile directory')
506        self.assure_is_dir('/var/run/shill/user_profiles',
507                           'Shill profile root')
508        self.assure_is_link_to('/var/run/shill/user_profiles/chronos',
509                               self.new_shill_user_profile_dir,
510                               'Shill profile link')
511        self.assure_is_dir(self.user_cryptohome_log_dir,
512                           'shill user log directory')
513        self.assure_is_link_to('/var/run/shill/log',
514                               self.user_cryptohome_log_dir,
515                               'Shill logs link')
516        self.assure_method_calls([[ 'CreateProfile', '~chronos/shill' ],
517                                  [ 'InsertUserProfile',
518                                    ('~chronos/shill', self.fake_user_hash) ]],
519                                 'CreateProfile and InsertUserProfile '
520                                 'are called')
521
522
523    def test_login_guest(self):
524        """ Tests the guest login process.
525
526        Login should create a temporary profile directory in /var/run,
527        instead of using one within the root directory for normal users.
528
529        """
530        os.mkdir('/var/run/shill')
531        self.login_guest()
532        self.assure(not os.path.exists(self.flimflam_user_profile),
533                    'Flimflam user profile does not exist')
534        self.assure(not os.path.exists(self.old_shill_user_profile),
535                    'Old shill user profile does not exist')
536        self.assure(not os.path.exists(self.new_shill_user_profile),
537                    'New shill user profile does not exist')
538        self.assure(not os.path.exists(self.new_shill_user_profile_dir),
539                    'New shill user profile directory')
540        self.assure_is_dir(self.guest_shill_user_profile_dir,
541                           'shill guest user profile directory')
542        self.assure_is_dir('/var/run/shill/user_profiles',
543                           'Shill profile root')
544        self.assure_is_link_to('/var/run/shill/user_profiles/chronos',
545                               self.guest_shill_user_profile_dir,
546                               'Shill profile link')
547        self.assure_is_dir(self.guest_shill_user_log_dir,
548                           'shill guest user log directory')
549        self.assure_is_link_to('/var/run/shill/log',
550                               self.guest_shill_user_log_dir,
551                               'Shill logs link')
552        self.assure_method_calls([[ 'CreateProfile', '~chronos/shill' ],
553                                  [ 'InsertUserProfile',
554                                    ('~chronos/shill', '') ]],
555                                 'CreateProfile and InsertUserProfile '
556                                 'are called')
557
558
559    def test_login_profile_exists(self):
560        """ Test logging in a user whose profile already exists.
561
562        Login script should only push (and not create) the user profile
563        if a user profile already exists.
564        """
565        os.mkdir('/var/run/shill')
566        os.mkdir(self.new_shill_user_profile_dir)
567        self.touch(self.new_shill_user_profile)
568        self.login()
569        self.assure_method_calls([[ 'InsertUserProfile',
570                                    ('~chronos/shill', self.fake_user_hash) ]],
571                                 'Only InsertUserProfile is called')
572
573
574    def test_login_old_shill_profile(self):
575        """ Test logging in a user with an old-style shill profile.
576
577        Login script should move an old shill user profile into place
578        if a new one does not exist.
579        """
580        os.mkdir('/var/run/shill')
581        self.create_old_shill_user_profile(self.magic_header)
582        self.login()
583        self.assure(not os.path.exists(self.old_shill_user_profile),
584                    'Old shill user profile no longer exists')
585        self.assure(not os.path.exists(self.old_shill_user_profile_dir),
586                    'Old shill user profile directory no longer exists')
587        self.assure_exists(self.new_shill_user_profile,
588                           'New shill profile')
589        self.assure(self.magic_header in
590                    self.file_contents(self.new_shill_user_profile),
591                    'Shill user profile contains our magic header')
592        self.assure_method_calls([[ 'InsertUserProfile',
593                                    ('~chronos/shill', self.fake_user_hash) ]],
594                                 'Only InsertUserProfile is called')
595
596
597    def make_symlink(self, path):
598        """ Create a symbolic link named |path|.
599
600        @param path string pathname of the symbolic link.
601
602        """
603        os.symlink('/etc/hosts', path)
604
605
606    def make_special_file(self, path):
607        """ Create a special file named |path|.
608
609        @param path string pathname of the special file.
610
611        """
612        os.mknod(path, stat.S_IFIFO)
613
614
615    def make_bad_owner(self, path):
616        """ Create a regular file with a strange ownership.
617
618        @param path string pathname of the file.
619
620        """
621        self.touch(path)
622        os.lchown(path, 1000, 1000)
623
624
625    def test_login_invalid_old_shill_profile(self):
626        """ Test logging in with an invalid old-style shill profile.
627
628        Login script should ignore non-regular files or files not owned
629        by the correct user.  The original file should be removed.
630
631        """
632        os.mkdir('/var/run/shill')
633        for file_creation_method in (self.make_symlink,
634                                     self.make_special_file,
635                                     os.mkdir,
636                                     self.make_bad_owner):
637            os.mkdir(self.old_shill_user_profile_dir)
638            file_creation_method(self.old_shill_user_profile)
639            self.login()
640            self.assure(not os.path.exists(self.old_shill_user_profile),
641                        'Old shill user profile no longer exists')
642            self.assure(not os.path.exists(self.old_shill_user_profile_dir),
643                        'Old shill user profile directory no longer exists')
644            self.assure(not os.path.exists(self.new_shill_user_profile),
645                        'New shill profile was not created')
646            self.assure_method_calls([[ 'CreateProfile', '~chronos/shill' ],
647                                      [ 'InsertUserProfile',
648                                        ('~chronos/shill',
649                                         self.fake_user_hash) ]],
650                                     'CreateProfile and InsertUserProfile '
651                                     'are called')
652            os.unlink('/var/run/shill/user_profiles/chronos')
653
654
655    def test_login_ignore_old_shill_profile(self):
656        """ Test logging in with both an old and new profile present.
657
658        Login script should ignore an old shill user profile if a new one
659        exists.
660
661        """
662        os.mkdir('/var/run/shill')
663        self.create_new_shill_user_profile('')
664        self.create_old_shill_user_profile(self.magic_header)
665        self.login()
666        self.assure(os.path.exists(self.old_shill_user_profile),
667                    'Old shill user profile still exists')
668        self.assure_exists(self.new_shill_user_profile,
669                           'New shill profile')
670        self.assure(self.magic_header not in
671                    self.file_contents(self.new_shill_user_profile),
672                    'Shill user profile does not contain our magic header')
673        self.assure_method_calls([[ 'InsertUserProfile',
674                                    ('~chronos/shill', self.fake_user_hash) ]],
675                                 'Only InsertUserProfile is called')
676
677
678    def test_login_flimflam_profile(self):
679        """ Test logging in with an old flimflam profile.
680
681        Login script should move a flimflam user profile into place
682        if a shill one does not exist.
683
684        """
685        os.mkdir('/var/run/shill')
686        self.create_flimflam_user_profile(self.magic_header)
687        self.login()
688        self.assure(not os.path.exists(self.flimflam_user_profile),
689                    'Flimflam user profile no longer exists')
690        self.assure(not os.path.exists(self.flimflam_user_profile_dir),
691                    'Flimflam user profile directory no longer exists')
692        self.assure_exists(self.new_shill_user_profile,
693                           'New shill profile')
694        self.assure(self.magic_header in
695                    self.file_contents(self.new_shill_user_profile),
696                    'Shill user profile contains our magic header')
697        self.assure_method_calls([[ 'InsertUserProfile',
698                                    ('~chronos/shill', self.fake_user_hash) ]],
699                                 'Only InsertUserProfile is called')
700
701
702    def test_login_ignore_flimflam_profile(self):
703        """ Test logging in with both a flimflam profile and a new profile.
704
705        Login script should ignore an old flimflam user profile if a new
706        one exists.
707
708        """
709        os.mkdir('/var/run/shill')
710        self.create_flimflam_user_profile(self.magic_header)
711        self.create_new_shill_user_profile('')
712        self.login()
713        self.assure_exists(self.new_shill_user_profile,
714                           'New shill profile')
715        self.assure(self.magic_header not in
716                    self.file_contents(self.new_shill_user_profile),
717                    'Shill user profile does not contain our magic header')
718        self.assure_method_calls([[ 'InsertUserProfile',
719                                    ('~chronos/shill', self.fake_user_hash) ]],
720                                 'Only InsertUserProfile is called')
721
722
723    def test_login_prefer_old_shill_profile(self):
724        """ Test logging in with both a flimflam and old-style shill profile.
725
726        Login script should use the old shill user profile in preference
727        to a flimflam user profile if the new user profile does not
728        exist.
729
730        """
731        os.mkdir('/var/run/shill')
732        self.create_flimflam_user_profile('')
733        self.create_old_shill_user_profile(self.magic_header)
734        self.login()
735        self.assure(not os.path.exists(self.flimflam_user_profile),
736                    'Flimflam user profile was removed')
737        self.assure(not os.path.exists(self.old_shill_user_profile),
738                    'Old shill user profile no longer exists')
739        self.assure_exists(self.new_shill_user_profile,
740                           'New shill profile')
741        self.assure(self.magic_header in
742                    self.file_contents(self.new_shill_user_profile),
743                    'Shill user profile contains our magic header')
744        self.assure_method_calls([[ 'InsertUserProfile',
745                                    ('~chronos/shill', self.fake_user_hash) ]],
746                                 'Only InsertUserProfile is called')
747
748
749    def test_login_multi_profile(self):
750        """ Test signalling shill about multiple logged-in users.
751
752        Login script should not create multiple profiles in parallel
753        if called more than once without an intervening logout.  Only
754        the initial user profile should be created.
755
756        """
757        os.mkdir('/var/run/shill')
758        self.create_new_shill_user_profile('')
759
760        # First logged-in user should create a profile (tested above).
761        self.login()
762
763        # Clear the mock method-call queue.
764        self.mock_flimflam.get_method_calls()
765
766        for attempt in range(5):
767            self.login()
768            self.assure_method_calls([], 'No more profiles are added to shill')
769            profile_links = os.listdir('/var/run/shill/user_profiles')
770            self.assure(len(profile_links) == 1, 'Only one profile exists')
771            self.assure(profile_links[0] == 'chronos',
772                        'The profile link is for the chronos user')
773            self.assure_is_link_to('/var/run/shill/log',
774                                   self.user_cryptohome_log_dir,
775                                   'Shill log link for chronos')
776
777
778    def test_logout(self):
779        """ Test the logout process. """
780        os.makedirs('/var/run/shill/user_profiles')
781        os.makedirs(self.guest_shill_user_profile_dir)
782        os.makedirs(self.guest_shill_user_log_dir)
783        self.touch('/var/run/state/logged-in')
784        self.logout()
785        self.assure(not os.path.exists('/var/run/shill/user_profiles'),
786                    'User profile directory was removed')
787        self.assure(not os.path.exists(self.guest_shill_user_profile_dir),
788                    'Guest user profile directory was removed')
789        self.assure(not os.path.exists(self.guest_shill_user_log_dir),
790                    'Guest user log directory was removed')
791        self.assure_method_calls([[ 'PopAllUserProfiles', '' ]],
792                                 'PopAllUserProfiles is called')
793