|
3 | 3 | from datetime import datetime |
4 | 4 | import base64 |
5 | 5 | import concurrent.futures |
6 | | -import contextlib # check if redundant |
| 6 | +import contextlib |
| 7 | +import errno |
7 | 8 | import hashlib |
8 | 9 | import io |
9 | 10 | import itertools |
@@ -2362,6 +2363,84 @@ def _update(n): |
2362 | 2363 |
|
2363 | 2364 | self.assertEqual(pbar.percent_done(), 100) |
2364 | 2365 |
|
| 2366 | + def test_replica_truncate_related_errors__issue_534(self): |
| 2367 | + sess = self.sess |
| 2368 | + data_objs = self.sess.data_objects |
| 2369 | + truncated_size = 16 |
| 2370 | + |
| 2371 | + try: |
| 2372 | + # Set path for a new, as yet nonexisting data object. |
| 2373 | + data_path = '{}/{}'.format( |
| 2374 | + helpers.home_collection(sess), |
| 2375 | + unique_name(my_function_name(), datetime.now(), truncated_size) + "issue_534_errors") |
| 2376 | + self.assertFalse (data_objs.exists(data_path), "Data object should not yet exist.") |
| 2377 | + |
| 2378 | + # Assert appropriate error is raised for the nonexisting object. |
| 2379 | + with self.assertRaises(ex.OBJ_PATH_DOES_NOT_EXIST): |
| 2380 | + data_objs.replica_truncate(data_path, truncated_size) |
| 2381 | + |
| 2382 | + d = data_objs.create(data_path) |
| 2383 | + |
| 2384 | + # Assert appropriate error is raised for an existing object without a replica on the given resource. |
| 2385 | + with self.create_simple_resc() as newResc: |
| 2386 | + with self.assertRaises(ex.SYS_REPLICA_INACCESSIBLE): |
| 2387 | + d.replica_truncate(truncated_size, **{kw.RESC_NAME_KW: newResc}) |
| 2388 | + |
| 2389 | + # Assert appropriate error is raised for an invalid (negative) size argument. |
| 2390 | + try: |
| 2391 | + data_objs.replica_truncate(data_path, -truncated_size) |
| 2392 | + except Exception as e: |
| 2393 | + self.assertIsInstance(e, ex.UNIX_FILE_TRUNCATE_ERR) |
| 2394 | + self.assertEqual('EINVAL', errno.errorcode[abs(e.code) % 1000]) |
| 2395 | + |
| 2396 | + finally: |
| 2397 | + if data_objs.exists(data_path): |
| 2398 | + data_objs.unlink(data_path, force = True) |
| 2399 | + |
| 2400 | + def test_replica_truncate__issue_534(self): |
| 2401 | + sess = self.sess |
| 2402 | + data_objs = self.sess.data_objects |
| 2403 | + original_size = 16 |
| 2404 | + original_content = b'_' * original_size |
| 2405 | + with self.create_simple_resc() as newResc: |
| 2406 | + for truncated_size in (original_size//2, original_size + 1024): |
| 2407 | + try: |
| 2408 | + data_path = '{}/{}'.format( |
| 2409 | + helpers.home_collection(sess), |
| 2410 | + unique_name(my_function_name(), datetime.now(), truncated_size) + "_issue_534") |
| 2411 | + |
| 2412 | + # Create a data object with size original_size. |
| 2413 | + with data_objs.open(data_path,'w') as f: |
| 2414 | + f.write(original_content) |
| 2415 | + d = data_objs.get(data_path) |
| 2416 | + |
| 2417 | + # Ensure there are two replicas, one on newResc as well as one on 'demoResc' |
| 2418 | + d.replicate(resource = newResc) |
| 2419 | + response = d.replica_truncate(truncated_size, **{kw.RESC_NAME_KW: newResc}) |
| 2420 | + |
| 2421 | + # Stat data object again. |
| 2422 | + d = data_objs.get(data_path) |
| 2423 | + |
| 2424 | + # Check that returned resource hierarchy and replica number match expectations. |
| 2425 | + self.assertEqual(response['replica_number'], |
| 2426 | + [_ for _ in data_objs.get(data_path).replicas if _.resource_name == newResc][0].number) |
| 2427 | + self.assertEqual(response['resource_hierarchy'], newResc) |
| 2428 | + |
| 2429 | + # Make sure sizes are as expected. |
| 2430 | + self.assertEqual([_ for _ in d.replicas if _.resource_name == newResc][0].size, truncated_size) |
| 2431 | + self.assertEqual([_ for _ in d.replicas if _.resource_name != newResc][0].size, original_size) |
| 2432 | + |
| 2433 | + # Check that content of truncated replicas is as expected. |
| 2434 | + if truncated_size <= original_size: |
| 2435 | + self.assertEqual( d.open('r').read(), original_content[:truncated_size]) |
| 2436 | + else: |
| 2437 | + self.assertEqual( d.open('r').read(), original_content + b'\0' * (truncated_size - original_size)) |
| 2438 | + |
| 2439 | + finally: |
| 2440 | + if data_objs.exists(data_path): |
| 2441 | + data_objs.unlink(data_path, force = True) |
| 2442 | + |
| 2443 | + |
2365 | 2444 | if __name__ == '__main__': |
2366 | 2445 | # let the tests find the parent irods lib |
2367 | 2446 | sys.path.insert(0, os.path.abspath('../..')) |
|
0 commit comments