| Index: third_party/gsutil/gslib/tests/test_tabcomplete.py
|
| diff --git a/third_party/gsutil/gslib/tests/test_tabcomplete.py b/third_party/gsutil/gslib/tests/test_tabcomplete.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..1069782eca6dfe2d7db4f68844adfbdd8ee0629d
|
| --- /dev/null
|
| +++ b/third_party/gsutil/gslib/tests/test_tabcomplete.py
|
| @@ -0,0 +1,355 @@
|
| +# -*- coding: utf-8 -*-
|
| +# Copyright 2014 Google Inc. All Rights Reserved.
|
| +#
|
| +# Licensed under the Apache License, Version 2.0 (the "License");
|
| +# you may not use this file except in compliance with the License.
|
| +# You may obtain a copy of the License at
|
| +#
|
| +# http://www.apache.org/licenses/LICENSE-2.0
|
| +#
|
| +# Unless required by applicable law or agreed to in writing, software
|
| +# distributed under the License is distributed on an "AS IS" BASIS,
|
| +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| +# See the License for the specific language governing permissions and
|
| +# limitations under the License.
|
| +"""Integration tests for tab completion."""
|
| +
|
| +from __future__ import absolute_import
|
| +
|
| +import os
|
| +import time
|
| +
|
| +from gslib.command import CreateGsutilLogger
|
| +from gslib.tab_complete import CloudObjectCompleter
|
| +from gslib.tab_complete import TAB_COMPLETE_CACHE_TTL
|
| +from gslib.tab_complete import TabCompletionCache
|
| +import gslib.tests.testcase as testcase
|
| +from gslib.tests.util import ARGCOMPLETE_AVAILABLE
|
| +from gslib.tests.util import SetBotoConfigForTest
|
| +from gslib.tests.util import unittest
|
| +from gslib.tests.util import WorkingDirectory
|
| +from gslib.util import GetTabCompletionCacheFilename
|
| +
|
| +
|
| +@unittest.skipUnless(ARGCOMPLETE_AVAILABLE,
|
| + 'Tab completion requires argcomplete')
|
| +class TestTabComplete(testcase.GsUtilIntegrationTestCase):
|
| + """Integration tests for tab completion."""
|
| +
|
| + def setUp(self):
|
| + super(TestTabComplete, self).setUp()
|
| + self.logger = CreateGsutilLogger('tab_complete')
|
| +
|
| + def test_single_bucket(self):
|
| + """Tests tab completion matching a single bucket."""
|
| +
|
| + bucket_base_name = self.MakeTempName('bucket')
|
| + bucket_name = bucket_base_name + '-suffix'
|
| + self.CreateBucket(bucket_name)
|
| +
|
| + request = '%s://%s' % (self.default_provider, bucket_base_name)
|
| + expected_result = '//%s/' % bucket_name
|
| +
|
| + self.RunGsUtilTabCompletion(['ls', request],
|
| + expected_results=[expected_result])
|
| +
|
| + def test_bucket_only_single_bucket(self):
|
| + """Tests bucket-only tab completion matching a single bucket."""
|
| +
|
| + bucket_base_name = self.MakeTempName('bucket')
|
| + bucket_name = bucket_base_name + '-s'
|
| + self.CreateBucket(bucket_name)
|
| +
|
| + request = '%s://%s' % (self.default_provider, bucket_base_name)
|
| + expected_result = '//%s ' % bucket_name
|
| +
|
| + self.RunGsUtilTabCompletion(['rb', request],
|
| + expected_results=[expected_result])
|
| +
|
| + def test_bucket_only_no_objects(self):
|
| + """Tests that bucket-only tab completion doesn't match objects."""
|
| +
|
| + object_base_name = self.MakeTempName('obj')
|
| + object_name = object_base_name + '-suffix'
|
| + object_uri = self.CreateObject(object_name=object_name, contents='data')
|
| +
|
| + request = '%s://%s/%s' % (
|
| + self.default_provider, object_uri.bucket_name, object_base_name)
|
| +
|
| + self.RunGsUtilTabCompletion(['rb', request], expected_results=[])
|
| +
|
| + def test_single_subdirectory(self):
|
| + """Tests tab completion matching a single subdirectory."""
|
| +
|
| + object_base_name = self.MakeTempName('obj')
|
| + object_name = object_base_name + '/subobj'
|
| + object_uri = self.CreateObject(object_name=object_name, contents='data')
|
| +
|
| + request = '%s://%s/' % (self.default_provider, object_uri.bucket_name)
|
| + expected_result = '//%s/%s/' % (object_uri.bucket_name, object_base_name)
|
| +
|
| + self.RunGsUtilTabCompletion(['ls', request],
|
| + expected_results=[expected_result])
|
| +
|
| + def test_multiple_buckets(self):
|
| + """Tests tab completion matching multiple buckets."""
|
| +
|
| + bucket_base_name = self.MakeTempName('bucket')
|
| + bucket1_name = bucket_base_name + '-suffix1'
|
| + self.CreateBucket(bucket1_name)
|
| + bucket2_name = bucket_base_name + '-suffix2'
|
| + self.CreateBucket(bucket2_name)
|
| +
|
| + request = '%s://%s' % (self.default_provider, bucket_base_name)
|
| + expected_result1 = '//%s/' % bucket1_name
|
| + expected_result2 = '//%s/' % bucket2_name
|
| +
|
| + self.RunGsUtilTabCompletion(['ls', request], expected_results=[
|
| + expected_result1, expected_result2])
|
| +
|
| + def test_single_object(self):
|
| + """Tests tab completion matching a single object."""
|
| +
|
| + object_base_name = self.MakeTempName('obj')
|
| + object_name = object_base_name + '-suffix'
|
| + object_uri = self.CreateObject(object_name=object_name, contents='data')
|
| +
|
| + request = '%s://%s/%s' % (
|
| + self.default_provider, object_uri.bucket_name, object_base_name)
|
| + expected_result = '//%s/%s ' % (object_uri.bucket_name, object_name)
|
| +
|
| + self.RunGsUtilTabCompletion(['ls', request],
|
| + expected_results=[expected_result])
|
| +
|
| + def test_multiple_objects(self):
|
| + """Tests tab completion matching multiple objects."""
|
| +
|
| + bucket_uri = self.CreateBucket()
|
| +
|
| + object_base_name = self.MakeTempName('obj')
|
| + object1_name = object_base_name + '-suffix1'
|
| + self.CreateObject(
|
| + bucket_uri=bucket_uri, object_name=object1_name, contents='data')
|
| + object2_name = object_base_name + '-suffix2'
|
| + self.CreateObject(
|
| + bucket_uri=bucket_uri, object_name=object2_name, contents='data')
|
| +
|
| + request = '%s://%s/%s' % (
|
| + self.default_provider, bucket_uri.bucket_name, object_base_name)
|
| + expected_result1 = '//%s/%s' % (bucket_uri.bucket_name, object1_name)
|
| + expected_result2 = '//%s/%s' % (bucket_uri.bucket_name, object2_name)
|
| +
|
| + self.RunGsUtilTabCompletion(['ls', request], expected_results=[
|
| + expected_result1, expected_result2])
|
| +
|
| + def test_subcommands(self):
|
| + """Tests tab completion for commands with subcommands."""
|
| +
|
| + bucket_base_name = self.MakeTempName('bucket')
|
| + bucket_name = bucket_base_name + '-suffix'
|
| + self.CreateBucket(bucket_name)
|
| +
|
| + bucket_request = '%s://%s' % (self.default_provider, bucket_base_name)
|
| + expected_bucket_result = '//%s ' % bucket_name
|
| +
|
| + local_file = 'a_local_file'
|
| + local_dir = self.CreateTempDir(test_files=[local_file])
|
| +
|
| + local_file_request = '%s%s' % (local_dir, os.sep)
|
| + expected_local_file_result = '%s ' % os.path.join(local_dir, local_file)
|
| +
|
| + # Should invoke Cloud bucket URL completer.
|
| + self.RunGsUtilTabCompletion(['cors', 'get', bucket_request],
|
| + expected_results=[expected_bucket_result])
|
| +
|
| + # Should invoke File URL completer which should match the local file.
|
| + self.RunGsUtilTabCompletion(['cors', 'set', local_file_request],
|
| + expected_results=[expected_local_file_result])
|
| +
|
| + # Should invoke Cloud bucket URL completer.
|
| + self.RunGsUtilTabCompletion(['cors', 'set', 'some_file', bucket_request],
|
| + expected_results=[expected_bucket_result])
|
| +
|
| + def test_invalid_partial_bucket_name(self):
|
| + """Tests tab completion with a partial URL that by itself is not valid.
|
| +
|
| + The bucket name in a Cloud URL cannot end in a dash, but a partial URL
|
| + during tab completion may end in a dash and completion should still work.
|
| + """
|
| +
|
| + bucket_base_name = self.MakeTempName('bucket')
|
| + bucket_name = bucket_base_name + '-s'
|
| + self.CreateBucket(bucket_name)
|
| +
|
| + request = '%s://%s-' % (self.default_provider, bucket_base_name)
|
| + expected_result = '//%s/' % bucket_name
|
| +
|
| + self.RunGsUtilTabCompletion(['ls', request],
|
| + expected_results=[expected_result])
|
| +
|
| + def test_acl_argument(self):
|
| + """Tests tab completion for ACL arguments."""
|
| +
|
| + local_file = 'a_local_file'
|
| + local_dir = self.CreateTempDir(test_files=[local_file])
|
| +
|
| + local_file_request = '%s%s' % (local_dir, os.sep)
|
| + expected_local_file_result = '%s ' % os.path.join(local_dir, local_file)
|
| +
|
| + # Should invoke File URL completer which should match the local file.
|
| + self.RunGsUtilTabCompletion(['acl', 'set', local_file_request],
|
| + expected_results=[expected_local_file_result])
|
| +
|
| + # Should match canned ACL name.
|
| + self.RunGsUtilTabCompletion(['acl', 'set', 'priv'],
|
| + expected_results=['private '])
|
| +
|
| + local_file = 'priv_file'
|
| + local_dir = self.CreateTempDir(test_files=[local_file])
|
| + with WorkingDirectory(local_dir):
|
| + # Should match both a file and a canned ACL since argument takes
|
| + # either one.
|
| + self.RunGsUtilTabCompletion(['acl', 'set', 'priv'],
|
| + expected_results=[local_file, 'private'])
|
| +
|
| +
|
| +def _WriteTabCompletionCache(prefix, results, timestamp=None,
|
| + partial_results=False):
|
| + if timestamp is None:
|
| + timestamp = time.time()
|
| + cache = TabCompletionCache(prefix, results, timestamp, partial_results)
|
| + cache.WriteToFile(GetTabCompletionCacheFilename())
|
| +
|
| +
|
| +@unittest.skipUnless(ARGCOMPLETE_AVAILABLE,
|
| + 'Tab completion requires argcomplete')
|
| +class TestTabCompleteUnitTests(testcase.unit_testcase.GsUtilUnitTestCase):
|
| + """Unit tests for tab completion."""
|
| +
|
| + def test_cached_results(self):
|
| + """Tests tab completion results returned from cache."""
|
| +
|
| + with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
|
| + request = 'gs://prefix'
|
| + cached_results = ['gs://prefix1', 'gs://prefix2']
|
| +
|
| + _WriteTabCompletionCache(request, cached_results)
|
| +
|
| + completer = CloudObjectCompleter(self.MakeGsUtilApi())
|
| + results = completer(request)
|
| +
|
| + self.assertEqual(cached_results, results)
|
| +
|
| + def test_expired_cached_results(self):
|
| + """Tests tab completion results not returned from cache when too old."""
|
| +
|
| + with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
|
| + bucket_base_name = self.MakeTempName('bucket')
|
| + bucket_name = bucket_base_name + '-suffix'
|
| + self.CreateBucket(bucket_name)
|
| +
|
| + request = '%s://%s' % (self.default_provider, bucket_base_name)
|
| + expected_result = '%s://%s/' % (self.default_provider, bucket_name)
|
| +
|
| + cached_results = ['//%s1' % bucket_name, '//%s2' % bucket_name]
|
| +
|
| + _WriteTabCompletionCache(request, cached_results,
|
| + time.time() - TAB_COMPLETE_CACHE_TTL)
|
| +
|
| + completer = CloudObjectCompleter(self.MakeGsUtilApi())
|
| + results = completer(request)
|
| +
|
| + self.assertEqual([expected_result], results)
|
| +
|
| + def test_prefix_caching(self):
|
| + """Tests tab completion results returned from cache with prefix match.
|
| +
|
| + If the tab completion prefix is an extension of the cached prefix, tab
|
| + completion should return results from the cache that start with the prefix.
|
| + """
|
| +
|
| + with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
|
| + cached_prefix = 'gs://prefix'
|
| + cached_results = ['gs://prefix-first', 'gs://prefix-second']
|
| + _WriteTabCompletionCache(cached_prefix, cached_results)
|
| +
|
| + request = 'gs://prefix-f'
|
| + completer = CloudObjectCompleter(self.MakeGsUtilApi())
|
| + results = completer(request)
|
| +
|
| + self.assertEqual(['gs://prefix-first'], results)
|
| +
|
| + def test_prefix_caching_boundary(self):
|
| + """Tests tab completion prefix caching not spanning directory boundaries.
|
| +
|
| + If the tab completion prefix is an extension of the cached prefix, but is
|
| + not within the same bucket/sub-directory then the cached results should not
|
| + be used.
|
| + """
|
| +
|
| + with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
|
| + object_uri = self.CreateObject(
|
| + object_name='subdir/subobj', contents='test data')
|
| +
|
| + cached_prefix = '%s://%s/' % (
|
| + self.default_provider, object_uri.bucket_name)
|
| + cached_results = ['%s://%s/subdir' % (
|
| + self.default_provider, object_uri.bucket_name)]
|
| + _WriteTabCompletionCache(cached_prefix, cached_results)
|
| +
|
| + request = '%s://%s/subdir/' % (
|
| + self.default_provider, object_uri.bucket_name)
|
| + expected_result = '%s://%s/subdir/subobj' % (
|
| + self.default_provider, object_uri.bucket_name)
|
| +
|
| + completer = CloudObjectCompleter(self.MakeGsUtilApi())
|
| + results = completer(request)
|
| +
|
| + self.assertEqual([expected_result], results)
|
| +
|
| + def test_prefix_caching_no_results(self):
|
| + """Tests tab completion returning empty result set using cached prefix.
|
| +
|
| + If the tab completion prefix is an extension of the cached prefix, but does
|
| + not match any of the cached results then no remote request should be made
|
| + and an empty result set should be returned.
|
| + """
|
| +
|
| + with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
|
| + object_uri = self.CreateObject(object_name='obj', contents='test data')
|
| +
|
| + cached_prefix = '%s://%s/' % (
|
| + self.default_provider, object_uri.bucket_name)
|
| + cached_results = []
|
| + _WriteTabCompletionCache(cached_prefix, cached_results)
|
| +
|
| + request = '%s://%s/o' % (self.default_provider, object_uri.bucket_name)
|
| +
|
| + completer = CloudObjectCompleter(self.MakeGsUtilApi())
|
| + results = completer(request)
|
| +
|
| + self.assertEqual([], results)
|
| +
|
| + def test_prefix_caching_partial_results(self):
|
| + """Tests tab completion prefix matching ignoring partial cached results.
|
| +
|
| + If the tab completion prefix is an extension of the cached prefix, but the
|
| + cached result set is partial, the cached results should not be used because
|
| + the matching results for the prefix may be incomplete.
|
| + """
|
| +
|
| + with SetBotoConfigForTest([('GSUtil', 'state_dir', self.CreateTempDir())]):
|
| + object_uri = self.CreateObject(object_name='obj', contents='test data')
|
| +
|
| + cached_prefix = '%s://%s/' % (
|
| + self.default_provider, object_uri.bucket_name)
|
| + cached_results = []
|
| + _WriteTabCompletionCache(cached_prefix, cached_results,
|
| + partial_results=True)
|
| +
|
| + request = '%s://%s/o' % (self.default_provider, object_uri.bucket_name)
|
| +
|
| + completer = CloudObjectCompleter(self.MakeGsUtilApi())
|
| + results = completer(request)
|
| +
|
| + self.assertEqual([str(object_uri)], results)
|
|
|