OLD | NEW |
| (Empty) |
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) | |
OLD | NEW |