Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/unit/customizations/s3/test_filegenerator.py
1569 views
1
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
import os
14
import platform
15
from awscli.testutils import mock, unittest, FileCreator, BaseAWSCommandParamsTest
16
from awscli.testutils import skip_if_windows
17
import stat
18
import tempfile
19
import shutil
20
import socket
21
22
from botocore.exceptions import ClientError
23
24
from awscli.customizations.s3.filegenerator import FileGenerator, \
25
FileDecodingError, FileStat, is_special_file, is_readable
26
from awscli.customizations.s3.utils import get_file_stat, EPOCH_TIME
27
from tests.unit.customizations.s3 import make_loc_files, clean_loc_files, \
28
compare_files
29
30
31
@skip_if_windows('Special files only supported on mac/linux')
32
class TestIsSpecialFile(unittest.TestCase):
33
def setUp(self):
34
self.files = FileCreator()
35
self.filename = 'foo'
36
37
def tearDown(self):
38
self.files.remove_all()
39
40
def test_is_character_device(self):
41
file_path = os.path.join(self.files.rootdir, self.filename)
42
self.files.create_file(self.filename, contents='')
43
with mock.patch('stat.S_ISCHR') as mock_class:
44
mock_class.return_value = True
45
self.assertTrue(is_special_file(file_path))
46
47
def test_is_block_device(self):
48
file_path = os.path.join(self.files.rootdir, self.filename)
49
self.files.create_file(self.filename, contents='')
50
with mock.patch('stat.S_ISBLK') as mock_class:
51
mock_class.return_value = True
52
self.assertTrue(is_special_file(file_path))
53
54
def test_is_fifo(self):
55
file_path = os.path.join(self.files.rootdir, self.filename)
56
mode = 0o600 | stat.S_IFIFO
57
os.mknod(file_path, mode)
58
self.assertTrue(is_special_file(file_path))
59
60
def test_is_socket(self):
61
file_path = os.path.join(self.files.rootdir, self.filename)
62
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
63
sock.bind(file_path)
64
self.assertTrue(is_special_file(file_path))
65
66
67
class TestIsReadable(unittest.TestCase):
68
def setUp(self):
69
self.files = FileCreator()
70
self.filename = 'foo'
71
self.full_path = os.path.join(self.files.rootdir, self.filename)
72
73
def tearDown(self):
74
self.files.remove_all()
75
76
def test_unreadable_file(self):
77
self.files.create_file(self.filename, contents="foo")
78
open_function = 'awscli.customizations.s3.filegenerator._open'
79
with mock.patch(open_function) as mock_class:
80
mock_class.side_effect = OSError()
81
self.assertFalse(is_readable(self.full_path))
82
83
def test_unreadable_directory(self):
84
os.mkdir(self.full_path)
85
with mock.patch('os.listdir') as mock_class:
86
mock_class.side_effect = OSError()
87
self.assertFalse(is_readable(self.full_path))
88
89
90
class LocalFileGeneratorTest(unittest.TestCase):
91
def setUp(self):
92
self.client = None
93
self.file_creator = FileCreator()
94
self.files = make_loc_files(self.file_creator)
95
self.local_file = self.files[0]
96
self.local_dir = self.files[3] + os.sep
97
98
def tearDown(self):
99
clean_loc_files(self.file_creator)
100
101
def test_local_file(self):
102
"""
103
Generate a single local file.
104
"""
105
input_local_file = {'src': {'path': self.local_file,
106
'type': 'local'},
107
'dest': {'path': 'bucket/text1.txt',
108
'type': 's3'},
109
'dir_op': False, 'use_src_name': False}
110
params = {'region': 'us-east-1'}
111
files = FileGenerator(self.client, '').call(input_local_file)
112
result_list = []
113
for filename in files:
114
result_list.append(filename)
115
size, last_update = get_file_stat(self.local_file)
116
file_stat = FileStat(src=self.local_file, dest='bucket/text1.txt',
117
compare_key='text1.txt', size=size,
118
last_update=last_update, src_type='local',
119
dest_type='s3', operation_name='')
120
ref_list = [file_stat]
121
self.assertEqual(len(result_list), len(ref_list))
122
for i in range(len(result_list)):
123
compare_files(self, result_list[i], ref_list[i])
124
125
def test_local_directory(self):
126
"""
127
Generate an entire local directory.
128
"""
129
input_local_dir = {'src': {'path': self.local_dir,
130
'type': 'local'},
131
'dest': {'path': 'bucket/',
132
'type': 's3'},
133
'dir_op': True, 'use_src_name': True}
134
params = {'region': 'us-east-1'}
135
files = FileGenerator(self.client, '').call(input_local_dir)
136
result_list = []
137
for filename in files:
138
result_list.append(filename)
139
size, last_update = get_file_stat(self.local_file)
140
file_stat = FileStat(src=self.local_file, dest='bucket/text1.txt',
141
compare_key='text1.txt', size=size,
142
last_update=last_update, src_type='local',
143
dest_type='s3', operation_name='')
144
path = self.local_dir + 'another_directory' + os.sep \
145
+ 'text2.txt'
146
size, last_update = get_file_stat(path)
147
file_stat2 = FileStat(src=path,
148
dest='bucket/another_directory/text2.txt',
149
compare_key='another_directory/text2.txt',
150
size=size, last_update=last_update,
151
src_type='local',
152
dest_type='s3', operation_name='')
153
ref_list = [file_stat2, file_stat]
154
self.assertEqual(len(result_list), len(ref_list))
155
for i in range(len(result_list)):
156
compare_files(self, result_list[i], ref_list[i])
157
158
159
@skip_if_windows('Symlink tests only supported on mac/linux')
160
class TestIgnoreFilesLocally(unittest.TestCase):
161
"""
162
This class tests the ability to ignore particular files. This includes
163
skipping symlink when desired.
164
"""
165
def setUp(self):
166
self.client = None
167
self.files = FileCreator()
168
169
def tearDown(self):
170
self.files.remove_all()
171
172
def test_warning(self):
173
path = os.path.join(self.files.rootdir, 'badsymlink')
174
os.symlink('non-existent-file', path)
175
filegenerator = FileGenerator(self.client, '', True)
176
self.assertTrue(filegenerator.should_ignore_file(path))
177
178
def test_skip_symlink(self):
179
filename = 'foo.txt'
180
self.files.create_file(os.path.join(self.files.rootdir,
181
filename),
182
contents='foo.txt contents')
183
sym_path = os.path.join(self.files.rootdir, 'symlink')
184
os.symlink(filename, sym_path)
185
filegenerator = FileGenerator(self.client, '', False)
186
self.assertTrue(filegenerator.should_ignore_file(sym_path))
187
188
def test_no_skip_symlink(self):
189
filename = 'foo.txt'
190
path = self.files.create_file(os.path.join(self.files.rootdir,
191
filename),
192
contents='foo.txt contents')
193
sym_path = os.path.join(self.files.rootdir, 'symlink')
194
os.symlink(path, sym_path)
195
filegenerator = FileGenerator(self.client, '', True)
196
self.assertFalse(filegenerator.should_ignore_file(sym_path))
197
self.assertFalse(filegenerator.should_ignore_file(path))
198
199
def test_no_skip_symlink_dir(self):
200
filename = 'dir'
201
path = os.path.join(self.files.rootdir, 'dir/')
202
os.mkdir(path)
203
sym_path = os.path.join(self.files.rootdir, 'symlink')
204
os.symlink(path, sym_path)
205
filegenerator = FileGenerator(self.client, '', True)
206
self.assertFalse(filegenerator.should_ignore_file(sym_path))
207
self.assertFalse(filegenerator.should_ignore_file(path))
208
209
210
class TestThrowsWarning(unittest.TestCase):
211
def setUp(self):
212
self.files = FileCreator()
213
self.root = self.files.rootdir
214
self.client = None
215
216
def tearDown(self):
217
self.files.remove_all()
218
219
def test_no_warning(self):
220
file_gen = FileGenerator(self.client, '', False)
221
self.files.create_file("foo.txt", contents="foo")
222
full_path = os.path.join(self.root, "foo.txt")
223
return_val = file_gen.triggers_warning(full_path)
224
self.assertFalse(return_val)
225
self.assertTrue(file_gen.result_queue.empty())
226
227
def test_no_exists(self):
228
file_gen = FileGenerator(self.client, '', False)
229
filename = os.path.join(self.root, 'file')
230
return_val = file_gen.triggers_warning(filename)
231
self.assertTrue(return_val)
232
warning_message = file_gen.result_queue.get()
233
self.assertEqual(warning_message.message,
234
("warning: Skipping file %s. File does not exist." %
235
filename))
236
237
def test_no_read_access(self):
238
file_gen = FileGenerator(self.client, '', False)
239
self.files.create_file("foo.txt", contents="foo")
240
full_path = os.path.join(self.root, "foo.txt")
241
open_function = 'awscli.customizations.s3.filegenerator._open'
242
with mock.patch(open_function) as mock_class:
243
mock_class.side_effect = OSError()
244
return_val = file_gen.triggers_warning(full_path)
245
self.assertTrue(return_val)
246
warning_message = file_gen.result_queue.get()
247
self.assertEqual(warning_message.message,
248
("warning: Skipping file %s. File/Directory is "
249
"not readable." % full_path))
250
251
@skip_if_windows('Special files only supported on mac/linux')
252
def test_is_special_file_warning(self):
253
file_gen = FileGenerator(self.client, '', False)
254
file_path = os.path.join(self.files.rootdir, 'foo')
255
# Use socket for special file.
256
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
257
sock.bind(file_path)
258
return_val = file_gen.triggers_warning(file_path)
259
self.assertTrue(return_val)
260
warning_message = file_gen.result_queue.get()
261
self.assertEqual(warning_message.message,
262
("warning: Skipping file %s. File is character "
263
"special device, block special device, FIFO, or "
264
"socket." % file_path))
265
266
267
@skip_if_windows('Symlink tests only supported on mac/linux')
268
class TestSymlinksIgnoreFiles(unittest.TestCase):
269
"""
270
This class tests the ability to list out the correct local files
271
depending on if symlinks are being followed. Also tests to ensure
272
broken symlinks fail.
273
"""
274
def setUp(self):
275
self.client = None
276
self.files = FileCreator()
277
# List of local filenames.
278
self.filenames = []
279
self.root = self.files.rootdir
280
self.bucket = 'bucket/'
281
filename_1 = self.files.create_file('foo.txt',
282
contents='foo.txt contents')
283
self.filenames.append(filename_1)
284
nested_dir = os.path.join(self.root, 'realfiles')
285
os.mkdir(nested_dir)
286
filename_2 = self.files.create_file(os.path.join(nested_dir,
287
'bar.txt'),
288
contents='bar.txt contents')
289
self.filenames.append(filename_2)
290
# Names of symlinks.
291
self.symlinks = []
292
# Names of files if symlinks are followed.
293
self.symlink_files = []
294
# Create symlink to file foo.txt.
295
symlink_1 = os.path.join(self.root, 'symlink_1')
296
os.symlink(filename_1, symlink_1)
297
self.symlinks.append(symlink_1)
298
self.symlink_files.append(symlink_1)
299
# Create a symlink to a file that does not exist.
300
symlink_2 = os.path.join(self.root, 'symlink_2')
301
os.symlink('non-existent-file', symlink_2)
302
self.symlinks.append(symlink_2)
303
# Create a symlink to directory realfiles
304
symlink_3 = os.path.join(self.root, 'symlink_3')
305
os.symlink(nested_dir, symlink_3)
306
self.symlinks.append(symlink_3)
307
self.symlink_files.append(os.path.join(symlink_3, 'bar.txt'))
308
309
def tearDown(self):
310
self.files.remove_all()
311
312
def test_no_follow_symlink(self):
313
abs_root = str(os.path.abspath(self.root) + os.sep)
314
input_local_dir = {'src': {'path': abs_root,
315
'type': 'local'},
316
'dest': {'path': self.bucket,
317
'type': 's3'},
318
'dir_op': True, 'use_src_name': True}
319
file_stats = FileGenerator(self.client, '', False).call(input_local_dir)
320
self.filenames.sort()
321
result_list = []
322
for file_stat in file_stats:
323
result_list.append(getattr(file_stat, 'src'))
324
self.assertEqual(len(result_list), len(self.filenames))
325
# Just check to make sure the right local files are generated.
326
for i in range(len(result_list)):
327
filename = str(os.path.abspath(self.filenames[i]))
328
self.assertEqual(result_list[i], filename)
329
330
def test_warn_bad_symlink(self):
331
"""
332
This tests to make sure it fails when following bad symlinks.
333
"""
334
abs_root = str(os.path.abspath(self.root) + os.sep)
335
input_local_dir = {'src': {'path': abs_root,
336
'type': 'local'},
337
'dest': {'path': self.bucket,
338
'type': 's3'},
339
'dir_op': True, 'use_src_name': True}
340
file_stats = FileGenerator(self.client, '', True).call(input_local_dir)
341
file_gen = FileGenerator(self.client, '', True)
342
file_stats = file_gen.call(input_local_dir)
343
all_filenames = self.filenames + self.symlink_files
344
all_filenames.sort()
345
result_list = []
346
for file_stat in file_stats:
347
result_list.append(getattr(file_stat, 'src'))
348
self.assertEqual(len(result_list), len(all_filenames))
349
# Just check to make sure the right local files are generated.
350
for i in range(len(result_list)):
351
filename = str(os.path.abspath(all_filenames[i]))
352
self.assertEqual(result_list[i], filename)
353
self.assertFalse(file_gen.result_queue.empty())
354
355
def test_follow_symlink(self):
356
# First remove the bad symlink.
357
os.remove(os.path.join(self.root, 'symlink_2'))
358
abs_root = str(os.path.abspath(self.root) + os.sep)
359
input_local_dir = {'src': {'path': abs_root,
360
'type': 'local'},
361
'dest': {'path': self.bucket,
362
'type': 's3'},
363
'dir_op': True, 'use_src_name': True}
364
file_stats = FileGenerator(self.client, '', True).call(input_local_dir)
365
all_filenames = self.filenames + self.symlink_files
366
all_filenames.sort()
367
result_list = []
368
for file_stat in file_stats:
369
result_list.append(getattr(file_stat, 'src'))
370
self.assertEqual(len(result_list), len(all_filenames))
371
# Just check to make sure the right local files are generated.
372
for i in range(len(result_list)):
373
filename = str(os.path.abspath(all_filenames[i]))
374
self.assertEqual(result_list[i], filename)
375
376
377
class TestListFilesLocally(unittest.TestCase):
378
maxDiff = None
379
380
def setUp(self):
381
self.directory = str(tempfile.mkdtemp())
382
383
def tearDown(self):
384
shutil.rmtree(self.directory)
385
386
@mock.patch('os.listdir')
387
def test_error_raised_on_decoding_error(self, listdir_mock):
388
# On Python3, sys.getdefaultencoding
389
file_generator = FileGenerator(None, None, None)
390
# utf-8 encoding for U+2713.
391
listdir_mock.return_value = [b'\xe2\x9c\x93']
392
list(file_generator.list_files(self.directory, dir_op=True))
393
# Ensure the message was added to the result queue and is
394
# being skipped.
395
self.assertFalse(file_generator.result_queue.empty())
396
warning_message = file_generator.result_queue.get()
397
self.assertIn("warning: Skipping file ", warning_message.message)
398
self.assertIn("Please check your locale settings.",
399
warning_message.message)
400
401
def test_list_files_is_in_sorted_order(self):
402
p = os.path.join
403
open(p(self.directory, 'test-123.txt'), 'w').close()
404
open(p(self.directory, 'test-321.txt'), 'w').close()
405
open(p(self.directory, 'test123.txt'), 'w').close()
406
open(p(self.directory, 'test321.txt'), 'w').close()
407
os.mkdir(p(self.directory, 'test'))
408
open(p(self.directory, 'test', 'foo.txt'), 'w').close()
409
410
file_generator = FileGenerator(None, None, None)
411
values = list(el[0] for el in file_generator.list_files(
412
self.directory, dir_op=True))
413
ref_vals = list(sorted(values,
414
key=lambda items: items.replace(os.sep, '/')))
415
self.assertEqual(values, ref_vals)
416
417
@mock.patch('awscli.customizations.s3.filegenerator.get_file_stat')
418
def test_list_files_with_invalid_timestamp(self, stat_mock):
419
stat_mock.return_value = 9, None
420
open(os.path.join(self.directory, 'test'), 'w').close()
421
file_generator = FileGenerator(None, None, None)
422
value = list(file_generator.list_files(self.directory, dir_op=True))[0]
423
self.assertIs(value[1]['LastModified'], EPOCH_TIME)
424
425
def test_list_local_files_with_unicode_chars(self):
426
p = os.path.join
427
open(p(self.directory, u'a'), 'w').close()
428
open(p(self.directory, u'a\u0300'), 'w').close()
429
open(p(self.directory, u'a\u0300-1'), 'w').close()
430
open(p(self.directory, u'a\u03001'), 'w').close()
431
open(p(self.directory, u'z'), 'w').close()
432
open(p(self.directory, u'\u00e6'), 'w').close()
433
os.mkdir(p(self.directory, u'a\u0300a'))
434
open(p(self.directory, u'a\u0300a', u'a'), 'w').close()
435
open(p(self.directory, u'a\u0300a', u'z'), 'w').close()
436
open(p(self.directory, u'a\u0300a', u'\u00e6'), 'w').close()
437
438
file_generator = FileGenerator(None, None, None)
439
values = list(el[0] for el in file_generator.list_files(
440
self.directory, dir_op=True))
441
expected_order = [os.path.join(self.directory, el) for el in [
442
u"a",
443
u"a\u0300",
444
u"a\u0300-1",
445
u"a\u03001",
446
u"a\u0300a%sa" % os.path.sep,
447
u"a\u0300a%sz" % os.path.sep,
448
u"a\u0300a%s\u00e6" % os.path.sep,
449
u"z",
450
u"\u00e6"
451
]]
452
self.assertEqual(values, expected_order)
453
454
455
class TestNormalizeSort(unittest.TestCase):
456
def test_normalize_sort(self):
457
names = ['xyz123456789',
458
'xyz1' + os.path.sep + 'test',
459
'xyz' + os.path.sep + 'test']
460
ref_names = [names[2], names[1], names[0]]
461
filegenerator = FileGenerator(None, None, None)
462
filegenerator.normalize_sort(names, os.path.sep, '/')
463
for i in range(len(ref_names)):
464
self.assertEqual(ref_names[i], names[i])
465
466
def test_normalize_sort_backslash(self):
467
names = ['xyz123456789',
468
'xyz1\\test',
469
'xyz\\test']
470
ref_names = [names[2], names[1], names[0]]
471
filegenerator = FileGenerator(None, None, None)
472
filegenerator.normalize_sort(names, '\\', '/')
473
for i in range(len(ref_names)):
474
self.assertEqual(ref_names[i], names[i])
475
476
477
class S3FileGeneratorTest(BaseAWSCommandParamsTest):
478
def setUp(self):
479
super(S3FileGeneratorTest, self).setUp()
480
self.client = self.driver.session.create_client('s3')
481
self.bucket = 'foo'
482
self.file1 = self.bucket + '/' + 'text1.txt'
483
self.file2 = self.bucket + '/' + 'another_directory/text2.txt'
484
485
def test_s3_file(self):
486
"""
487
Generate a single s3 file
488
Note: Size and last update are not tested because s3 generates them.
489
"""
490
input_s3_file = {'src': {'path': self.file1, 'type': 's3'},
491
'dest': {'path': 'text1.txt', 'type': 'local'},
492
'dir_op': False, 'use_src_name': False}
493
params = {'region': 'us-east-1'}
494
self.parsed_responses = [{"ETag": "abcd", "ContentLength": 100,
495
"LastModified": "2014-01-09T20:45:49.000Z"}]
496
self.patch_make_request()
497
498
file_gen = FileGenerator(self.client, '')
499
files = file_gen.call(input_s3_file)
500
result_list = []
501
for filename in files:
502
result_list.append(filename)
503
file_stat = FileStat(src=self.file1, dest='text1.txt',
504
compare_key='text1.txt',
505
size=result_list[0].size,
506
last_update=result_list[0].last_update,
507
src_type='s3',
508
dest_type='local', operation_name='')
509
510
ref_list = [file_stat]
511
self.assertEqual(len(result_list), len(ref_list))
512
for i in range(len(result_list)):
513
compare_files(self, result_list[i], ref_list[i])
514
515
def test_s3_single_file_404(self):
516
"""
517
Test the error message for a 404 ClientError for a single file listing
518
"""
519
input_s3_file = {'src': {'path': self.file1, 'type': 's3'},
520
'dest': {'path': 'text1.txt', 'type': 'local'},
521
'dir_op': False, 'use_src_name': False}
522
params = {'region': 'us-east-1'}
523
self.client = mock.Mock()
524
self.client.head_object.side_effect = \
525
ClientError(
526
{'Error': {'Code': '404', 'Message': 'Not Found'}},
527
'HeadObject',
528
)
529
file_gen = FileGenerator(self.client, '')
530
files = file_gen.call(input_s3_file)
531
# The error should include 404 and should include the key name.
532
with self.assertRaisesRegex(ClientError, '404.*text1.txt'):
533
list(files)
534
535
def test_s3_single_file_delete(self):
536
input_s3_file = {'src': {'path': self.file1, 'type': 's3'},
537
'dest': {'path': '', 'type': 'local'},
538
'dir_op': False, 'use_src_name': True}
539
self.client = mock.Mock()
540
file_gen = FileGenerator(self.client, 'delete')
541
result_list = list(file_gen.call(input_s3_file))
542
self.assertEqual(len(result_list), 1)
543
compare_files(
544
self,
545
result_list[0],
546
FileStat(src=self.file1, dest='text1.txt',
547
compare_key='text1.txt',
548
size=None, last_update=None,
549
src_type='s3', dest_type='local',
550
operation_name='delete')
551
)
552
self.client.head_object.assert_not_called()
553
554
def test_s3_directory(self):
555
"""
556
Generates s3 files under a common prefix. Also it ensures that
557
zero size files are ignored.
558
Note: Size and last update are not tested because s3 generates them.
559
"""
560
input_s3_file = {'src': {'path': self.bucket + '/', 'type': 's3'},
561
'dest': {'path': '', 'type': 'local'},
562
'dir_op': True, 'use_src_name': True}
563
params = {'region': 'us-east-1'}
564
files = FileGenerator(self.client, '').call(input_s3_file)
565
566
self.parsed_responses = [{
567
"CommonPrefixes": [], "Contents": [
568
{"Key": "another_directory/text2.txt", "Size": 100,
569
"LastModified": "2014-01-09T20:45:49.000Z"},
570
{"Key": "text1.txt", "Size": 10,
571
"LastModified": "2013-01-09T20:45:49.000Z"}]}]
572
self.patch_make_request()
573
result_list = []
574
for filename in files:
575
result_list.append(filename)
576
file_stat = FileStat(src=self.file2,
577
dest='another_directory' + os.sep +
578
'text2.txt',
579
compare_key='another_directory/text2.txt',
580
size=result_list[0].size,
581
last_update=result_list[0].last_update,
582
src_type='s3',
583
dest_type='local', operation_name='')
584
file_stat2 = FileStat(src=self.file1,
585
dest='text1.txt',
586
compare_key='text1.txt',
587
size=result_list[1].size,
588
last_update=result_list[1].last_update,
589
src_type='s3',
590
dest_type='local', operation_name='')
591
592
ref_list = [file_stat, file_stat2]
593
self.assertEqual(len(result_list), len(ref_list))
594
for i in range(len(result_list)):
595
compare_files(self, result_list[i], ref_list[i])
596
597
def test_s3_delete_directory(self):
598
"""
599
Generates s3 files under a common prefix. Also it ensures that
600
the directory itself is included because it is a delete command
601
Note: Size and last update are not tested because s3 generates them.
602
"""
603
input_s3_file = {'src': {'path': self.bucket + '/', 'type': 's3'},
604
'dest': {'path': '', 'type': 'local'},
605
'dir_op': True, 'use_src_name': True}
606
self.parsed_responses = [{
607
"CommonPrefixes": [], "Contents": [
608
{"Key": "another_directory/", "Size": 0,
609
"LastModified": "2012-01-09T20:45:49.000Z"},
610
{"Key": "another_directory/text2.txt", "Size": 100,
611
"LastModified": "2014-01-09T20:45:49.000Z"},
612
{"Key": "text1.txt", "Size": 10,
613
"LastModified": "2013-01-09T20:45:49.000Z"}]}]
614
self.patch_make_request()
615
files = FileGenerator(self.client, 'delete').call(input_s3_file)
616
result_list = []
617
for filename in files:
618
result_list.append(filename)
619
620
file_stat1 = FileStat(src=self.bucket + '/another_directory/',
621
dest='another_directory' + os.sep,
622
compare_key='another_directory/',
623
size=result_list[0].size,
624
last_update=result_list[0].last_update,
625
src_type='s3',
626
dest_type='local', operation_name='delete')
627
file_stat2 = FileStat(src=self.file2,
628
dest='another_directory' + os.sep + 'text2.txt',
629
compare_key='another_directory/text2.txt',
630
size=result_list[1].size,
631
last_update=result_list[1].last_update,
632
src_type='s3',
633
dest_type='local', operation_name='delete')
634
file_stat3 = FileStat(src=self.file1,
635
dest='text1.txt',
636
compare_key='text1.txt',
637
size=result_list[2].size,
638
last_update=result_list[2].last_update,
639
src_type='s3',
640
dest_type='local', operation_name='delete')
641
642
ref_list = [file_stat1, file_stat2, file_stat3]
643
self.assertEqual(len(result_list), len(ref_list))
644
for i in range(len(result_list)):
645
compare_files(self, result_list[i], ref_list[i])
646
647
648
if __name__ == "__main__":
649
unittest.main()
650
651