Home | History | Annotate | Download | only in tests
      1 # -*- coding: utf-8 -*-
      2 # Copyright 2014 Google Inc.  All Rights Reserved.
      3 #
      4 # Licensed under the Apache License, Version 2.0 (the "License");
      5 # you may not use this file except in compliance with the License.
      6 # You may obtain a copy of the License at
      7 #
      8 #     http://www.apache.org/licenses/LICENSE-2.0
      9 #
     10 # Unless required by applicable law or agreed to in writing, software
     11 # distributed under the License is distributed on an "AS IS" BASIS,
     12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 # See the License for the specific language governing permissions and
     14 # limitations under the License.
     15 """Integration tests for tab completion."""
     16 
     17 from __future__ import absolute_import
     18 
     19 import os
     20 import time
     21 
     22 from gslib.command import CreateGsutilLogger
     23 from gslib.tab_complete import CloudObjectCompleter
     24 from gslib.tab_complete import TAB_COMPLETE_CACHE_TTL
     25 from gslib.tab_complete import TabCompletionCache
     26 import gslib.tests.testcase as testcase
     27 from gslib.tests.util import ARGCOMPLETE_AVAILABLE
     28 from gslib.tests.util import SetBotoConfigForTest
     29 from gslib.tests.util import unittest
     30 from gslib.tests.util import WorkingDirectory
     31 from gslib.util import GetTabCompletionCacheFilename
     32 
     33 
     34 @unittest.skipUnless(ARGCOMPLETE_AVAILABLE,
     35                      'Tab completion requires argcomplete')
     36 class TestTabComplete(testcase.GsUtilIntegrationTestCase):
     37   """Integration tests for tab completion."""
     38 
     39   def setUp(self):
     40     super(TestTabComplete, self).setUp()
     41     self.logger = CreateGsutilLogger('tab_complete')
     42 
     43   def test_single_bucket(self):
     44     """Tests tab completion matching a single bucket."""
     45 
     46     bucket_base_name = self.MakeTempName('bucket')
     47     bucket_name = bucket_base_name + '-suffix'
     48     self.CreateBucket(bucket_name)
     49 
     50     request = '%s://%s' % (self.default_provider, bucket_base_name)
     51     expected_result = '//%s/' % bucket_name
     52 
     53     self.RunGsUtilTabCompletion(['ls', request],
     54                                 expected_results=[expected_result])
     55 
     56   def test_bucket_only_single_bucket(self):
     57     """Tests bucket-only tab completion matching a single bucket."""
     58 
     59     bucket_base_name = self.MakeTempName('bucket')
     60     bucket_name = bucket_base_name + '-s'
     61     self.CreateBucket(bucket_name)
     62 
     63     request = '%s://%s' % (self.default_provider, bucket_base_name)
     64     expected_result = '//%s ' % bucket_name
     65 
     66     self.RunGsUtilTabCompletion(['rb', request],
     67                                 expected_results=[expected_result])
     68 
     69   def test_bucket_only_no_objects(self):
     70     """Tests that bucket-only tab completion doesn't match objects."""
     71 
     72     object_base_name = self.MakeTempName('obj')
     73     object_name = object_base_name + '-suffix'
     74     object_uri = self.CreateObject(object_name=object_name, contents='data')
     75 
     76     request = '%s://%s/%s' % (
     77         self.default_provider, object_uri.bucket_name, object_base_name)
     78 
     79     self.RunGsUtilTabCompletion(['rb', request], expected_results=[])
     80 
     81   def test_single_subdirectory(self):
     82     """Tests tab completion matching a single subdirectory."""
     83 
     84     object_base_name = self.MakeTempName('obj')
     85     object_name = object_base_name + '/subobj'
     86     object_uri = self.CreateObject(object_name=object_name, contents='data')
     87 
     88     request = '%s://%s/' % (self.default_provider, object_uri.bucket_name)
     89     expected_result = '//%s/%s/' % (object_uri.bucket_name, object_base_name)
     90 
     91     self.RunGsUtilTabCompletion(['ls', request],
     92                                 expected_results=[expected_result])
     93 
     94   def test_multiple_buckets(self):
     95     """Tests tab completion matching multiple buckets."""
     96 
     97     bucket_base_name = self.MakeTempName('bucket')
     98     bucket1_name = bucket_base_name + '-suffix1'
     99     self.CreateBucket(bucket1_name)
    100     bucket2_name = bucket_base_name + '-suffix2'
    101     self.CreateBucket(bucket2_name)
    102 
    103     request = '%s://%s' % (self.default_provider, bucket_base_name)
    104     expected_result1 = '//%s/' % bucket1_name
    105     expected_result2 = '//%s/' % bucket2_name
    106 
    107     self.RunGsUtilTabCompletion(['ls', request], expected_results=[
    108         expected_result1, expected_result2])
    109 
    110   def test_single_object(self):
    111     """Tests tab completion matching a single object."""
    112 
    113     object_base_name = self.MakeTempName('obj')
    114     object_name = object_base_name + '-suffix'
    115     object_uri = self.CreateObject(object_name=object_name, contents='data')
    116 
    117     request = '%s://%s/%s' % (
    118         self.default_provider, object_uri.bucket_name, object_base_name)
    119     expected_result = '//%s/%s ' % (object_uri.bucket_name, object_name)
    120 
    121     self.RunGsUtilTabCompletion(['ls', request],
    122                                 expected_results=[expected_result])
    123 
    124   def test_multiple_objects(self):
    125     """Tests tab completion matching multiple objects."""
    126 
    127     bucket_uri = self.CreateBucket()
    128 
    129     object_base_name = self.MakeTempName('obj')
    130     object1_name = object_base_name + '-suffix1'
    131     self.CreateObject(
    132         bucket_uri=bucket_uri, object_name=object1_name, contents='data')
    133     object2_name = object_base_name + '-suffix2'
    134     self.CreateObject(
    135         bucket_uri=bucket_uri, object_name=object2_name, contents='data')
    136 
    137     request = '%s://%s/%s' % (
    138         self.default_provider, bucket_uri.bucket_name, object_base_name)
    139     expected_result1 = '//%s/%s' % (bucket_uri.bucket_name, object1_name)
    140     expected_result2 = '//%s/%s' % (bucket_uri.bucket_name, object2_name)
    141 
    142     self.RunGsUtilTabCompletion(['ls', request], expected_results=[
    143         expected_result1, expected_result2])
    144 
    145   def test_subcommands(self):
    146     """Tests tab completion for commands with subcommands."""
    147 
    148     bucket_base_name = self.MakeTempName('bucket')
    149     bucket_name = bucket_base_name + '-suffix'
    150     self.CreateBucket(bucket_name)
    151 
    152     bucket_request = '%s://%s' % (self.default_provider, bucket_base_name)
    153     expected_bucket_result = '//%s ' % bucket_name
    154 
    155     local_file = 'a_local_file'
    156     local_dir = self.CreateTempDir(test_files=[local_file])
    157 
    158     local_file_request = '%s%s' % (local_dir, os.sep)
    159     expected_local_file_result = '%s ' % os.path.join(local_dir, local_file)
    160 
    161     # Should invoke Cloud bucket URL completer.
    162     self.RunGsUtilTabCompletion(['cors', 'get', bucket_request],
    163                                 expected_results=[expected_bucket_result])
    164 
    165     # Should invoke File URL completer which should match the local file.
    166     self.RunGsUtilTabCompletion(['cors', 'set', local_file_request],
    167                                 expected_results=[expected_local_file_result])
    168 
    169     # Should invoke Cloud bucket URL completer.
    170     self.RunGsUtilTabCompletion(['cors', 'set', 'some_file', bucket_request],
    171                                 expected_results=[expected_bucket_result])
    172 
    173   def test_invalid_partial_bucket_name(self):
    174     """Tests tab completion with a partial URL that by itself is not valid.
    175 
    176     The bucket name in a Cloud URL cannot end in a dash, but a partial URL
    177     during tab completion may end in a dash and completion should still work.
    178     """
    179 
    180     bucket_base_name = self.MakeTempName('bucket')
    181     bucket_name = bucket_base_name + '-s'
    182     self.CreateBucket(bucket_name)
    183 
    184     request = '%s://%s-' % (self.default_provider, bucket_base_name)
    185     expected_result = '//%s/' % bucket_name
    186 
    187     self.RunGsUtilTabCompletion(['ls', request],
    188                                 expected_results=[expected_result])
    189 
    190   def test_acl_argument(self):
    191     """Tests tab completion for ACL arguments."""
    192 
    193     local_file = 'a_local_file'
    194     local_dir = self.CreateTempDir(test_files=[local_file])
    195 
    196     local_file_request = '%s%s' % (local_dir, os.sep)
    197     expected_local_file_result = '%s ' % os.path.join(local_dir, local_file)
    198 
    199     # Should invoke File URL completer which should match the local file.
    200     self.RunGsUtilTabCompletion(['acl', 'set', local_file_request],
    201                                 expected_results=[expected_local_file_result])
    202 
    203     # Should match canned ACL name.
    204     self.RunGsUtilTabCompletion(['acl', 'set', 'priv'],
    205                                 expected_results=['private '])
    206 
    207     local_file = 'priv_file'
    208     local_dir = self.CreateTempDir(test_files=[local_file])
    209     with WorkingDirectory(local_dir):
    210       # Should match both a file and a canned ACL since argument takes
    211       # either one.
    212       self.RunGsUtilTabCompletion(['acl', 'set', 'priv'],
    213                                   expected_results=[local_file, 'private'])
    214 
    215 
    216 def _WriteTabCompletionCache(prefix, results, timestamp=None,
    217                              partial_results=False):
    218   if timestamp is None:
    219     timestamp = time.time()
    220   cache = TabCompletionCache(prefix, results, timestamp, partial_results)
    221   cache.WriteToFile(GetTabCompletionCacheFilename())
    222 
    223 
    224 @unittest.skipUnless(ARGCOMPLETE_AVAILABLE,
    225                      'Tab completion requires argcomplete')
    226 class TestTabCompleteUnitTests(testcase.unit_testcase.GsUtilUnitTestCase):
    227   """Unit tests for tab completion."""
    228 
    229   def test_cached_results(self):
    230     """Tests tab completion results returned from cache."""
    231 
    232     with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
    233       request = 'gs://prefix'
    234       cached_results = ['gs://prefix1', 'gs://prefix2']
    235 
    236       _WriteTabCompletionCache(request, cached_results)
    237 
    238       completer = CloudObjectCompleter(self.MakeGsUtilApi())
    239       results = completer(request)
    240 
    241       self.assertEqual(cached_results, results)
    242 
    243   def test_expired_cached_results(self):
    244     """Tests tab completion results not returned from cache when too old."""
    245 
    246     with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
    247       bucket_base_name = self.MakeTempName('bucket')
    248       bucket_name = bucket_base_name + '-suffix'
    249       self.CreateBucket(bucket_name)
    250 
    251       request = '%s://%s' % (self.default_provider, bucket_base_name)
    252       expected_result = '%s://%s/' % (self.default_provider, bucket_name)
    253 
    254       cached_results = ['//%s1' % bucket_name, '//%s2' % bucket_name]
    255 
    256       _WriteTabCompletionCache(request, cached_results,
    257                                time.time() - TAB_COMPLETE_CACHE_TTL)
    258 
    259       completer = CloudObjectCompleter(self.MakeGsUtilApi())
    260       results = completer(request)
    261 
    262       self.assertEqual([expected_result], results)
    263 
    264   def test_prefix_caching(self):
    265     """Tests tab completion results returned from cache with prefix match.
    266 
    267     If the tab completion prefix is an extension of the cached prefix, tab
    268     completion should return results from the cache that start with the prefix.
    269     """
    270 
    271     with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
    272       cached_prefix = 'gs://prefix'
    273       cached_results = ['gs://prefix-first', 'gs://prefix-second']
    274       _WriteTabCompletionCache(cached_prefix, cached_results)
    275 
    276       request = 'gs://prefix-f'
    277       completer = CloudObjectCompleter(self.MakeGsUtilApi())
    278       results = completer(request)
    279 
    280       self.assertEqual(['gs://prefix-first'], results)
    281 
    282   def test_prefix_caching_boundary(self):
    283     """Tests tab completion prefix caching not spanning directory boundaries.
    284 
    285     If the tab completion prefix is an extension of the cached prefix, but is
    286     not within the same bucket/sub-directory then the cached results should not
    287     be used.
    288     """
    289 
    290     with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
    291       object_uri = self.CreateObject(
    292           object_name='subdir/subobj', contents='test data')
    293 
    294       cached_prefix = '%s://%s/' % (
    295           self.default_provider, object_uri.bucket_name)
    296       cached_results = ['%s://%s/subdir' % (
    297           self.default_provider, object_uri.bucket_name)]
    298       _WriteTabCompletionCache(cached_prefix, cached_results)
    299 
    300       request = '%s://%s/subdir/' % (
    301           self.default_provider, object_uri.bucket_name)
    302       expected_result = '%s://%s/subdir/subobj' % (
    303           self.default_provider, object_uri.bucket_name)
    304 
    305       completer = CloudObjectCompleter(self.MakeGsUtilApi())
    306       results = completer(request)
    307 
    308       self.assertEqual([expected_result], results)
    309 
    310   def test_prefix_caching_no_results(self):
    311     """Tests tab completion returning empty result set using cached prefix.
    312 
    313     If the tab completion prefix is an extension of the cached prefix, but does
    314     not match any of the cached results then no remote request should be made
    315     and an empty result set should be returned.
    316     """
    317 
    318     with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
    319       object_uri = self.CreateObject(object_name='obj', contents='test data')
    320 
    321       cached_prefix = '%s://%s/' % (
    322           self.default_provider, object_uri.bucket_name)
    323       cached_results = []
    324       _WriteTabCompletionCache(cached_prefix, cached_results)
    325 
    326       request = '%s://%s/o' % (self.default_provider, object_uri.bucket_name)
    327 
    328       completer = CloudObjectCompleter(self.MakeGsUtilApi())
    329       results = completer(request)
    330 
    331       self.assertEqual([], results)
    332 
    333   def test_prefix_caching_partial_results(self):
    334     """Tests tab completion prefix matching ignoring partial cached results.
    335 
    336     If the tab completion prefix is an extension of the cached prefix, but the
    337     cached result set is partial, the cached results should not be used because
    338     the matching results for the prefix may be incomplete.
    339     """
    340 
    341     with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
    342       object_uri = self.CreateObject(object_name='obj', contents='test data')
    343 
    344       cached_prefix = '%s://%s/' % (
    345           self.default_provider, object_uri.bucket_name)
    346       cached_results = []
    347       _WriteTabCompletionCache(cached_prefix, cached_results,
    348                                partial_results=True)
    349 
    350       request = '%s://%s/o' % (self.default_provider, object_uri.bucket_name)
    351 
    352       completer = CloudObjectCompleter(self.MakeGsUtilApi())
    353       results = completer(request)
    354 
    355       self.assertEqual([str(object_uri)], results)
    356