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