diff options
author | Kevin Greenan <kmgreen2@gmail.com> | 2014-06-13 12:25:23 -0700 |
---|---|---|
committer | Kevin Greenan <kmgreen2@gmail.com> | 2014-06-13 12:25:23 -0700 |
commit | 200fba20f711fc4f2d28661e64d95462ce6f9500 (patch) | |
tree | 3fc56aed96f7011dc569661c19332e3e8671ac82 | |
parent | 0f7f2b3dcf4f3ceb4ad9acebfbd4baf61836b6d1 (diff) | |
parent | a1332c135ebaa2f168a256b0a92dc0b9f654f57f (diff) | |
download | pyeclib-200fba20f711fc4f2d28661e64d95462ce6f9500.tar.gz |
Merged in issue-44 (pull request #8)
Unit test refactor
-rw-r--r-- | setup.py | 3 | ||||
-rw-r--r-- | src/python/test/pyeclib_test.py | 356 | ||||
-rw-r--r-- | test/__init__.py | 0 | ||||
-rw-r--r-- | test/run_tests.py | 82 | ||||
-rw-r--r-- | test/test_core.py | 110 | ||||
-rw-r--r-- | test/test_pyeclib_api.py (renamed from src/python/pyeclib/ec_test.py) | 291 | ||||
-rw-r--r-- | test/test_pyeclib_c.py | 419 |
7 files changed, 641 insertions, 620 deletions
@@ -223,4 +223,5 @@ setup(name='PyECLib', packages=['pyeclib'], package_dir={'pyeclib': 'src/python/pyeclib'}, cmdclass={'build': build, 'install': install, 'clean': clean}, - py_modules=['pyeclib.ec_iface', 'pyeclib.core']) + py_modules=['pyeclib.ec_iface', 'pyeclib.core'], + test_suite='test') diff --git a/src/python/test/pyeclib_test.py b/src/python/test/pyeclib_test.py deleted file mode 100644 index cabf28e..0000000 --- a/src/python/test/pyeclib_test.py +++ /dev/null @@ -1,356 +0,0 @@ -# Copyright (c) 2013, Kevin Greenan (kmgreen2@gmail.com) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. THIS SOFTWARE IS -# PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS -# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN -# NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import pyeclib_c -import time -import random -import sys -import os -import codecs - -from string import ascii_letters - - -class Timer: - - def __init__(self): - self.start_time = 0 - self.end_time = 0 - - def start(self): - self.start_time = time.time() - - def stop(self): - self.end_time = time.time() - - def curr_delta(self): - return self.end_time - self.start_time - - def stop_and_return(self): - self.end_time = time.time() - return self.curr_delta() - - -def setup(file_sizes): - if not os.path.isdir("test_files"): - os.mkdir("./test_files") - - for size_str in file_sizes: - - size_desc = size_str.split("-") - - size = int(size_desc[0]) - - if (size_desc[1] == 'M'): - size *= 1000000 - elif (size_desc[1] == 'K'): - size *= 1000 - - testdata = ''.join(random.choice(ascii_letters) for i in range(size)) - testdata_bytes = testdata.encode('utf-8') - - filename = "test_file.%s" % size_str - with open(("test_files/%s" % filename), "wb") as fp: - fp.write(testdata_bytes) - - -def cleanup(file_sizes): - for size_str in file_sizes: - filename = "test_files/test_file.%s" % size_str - os.unlink(filename) - os.rmdir("./test_files") - - -def time_encode(num_data, num_parity, w, ec_type, file_size, iterations): - timer = Timer() - tsum = 0 - handle = pyeclib_c.init(num_data, num_parity, w, ec_type) - - filename = "test_file.%s" % file_size - with open("test_files/%s" % filename, "rb") as fp: - whole_file_bytes = fp.read() - - timer.start() - for l in range(iterations): - fragments = pyeclib_c.encode(handle, whole_file_bytes) - tsum = timer.stop_and_return() - - return tsum / iterations - - -def time_decode(num_data, num_parity, w, ec_type, file_size, iterations, hd): - timer = Timer() - tsum = 0 - handle = pyeclib_c.init(num_data, num_parity, w, ec_type) - - filename = "test_file.%s" % file_size - with open("test_files/%s" % filename, "rb") as fp: - whole_file_bytes = fp.read() - - fragments = pyeclib_c.encode(handle, whole_file_bytes) - - orig_fragments = fragments[:] - - for i in range(iterations): - missing_idxs = [] - num_missing = hd - 1 - for j in range(num_missing): - idx = random.randint(0, (num_data + num_parity) - 1) - while idx in missing_idxs: - idx = random.randint(0, (num_data + num_parity) - 1) - missing_idxs.append(idx) - fragments[idx] = b'\0' * len(fragments[0]) - - timer.start() - decoded_fragments = pyeclib_c.decode( - handle, fragments[ - :num_data], fragments[ - num_data:], missing_idxs, len( - fragments[0])) - tsum += timer.stop_and_return() - - fragments = decoded_fragments - - for j in range(num_data + num_parity): - if orig_fragments[j] != decoded_fragments[j]: - with open("orig_fragments", "wb") as fd_orig: - fd_orig.write(orig_fragments[j]) - - with open("decoded_fragments", "wb") as fd_decoded: - fd_decoded.write(decoded_fragments[j]) - - print(("Fragment %d was not reconstructed!!!" % j)) - sys.exit(2) - - decoded_fragments = None - - return tsum / iterations - - -def test_reconstruct(num_data, num_parity, w, ec_type, file_size, iterations): - timer = Timer() - tsum = 0 - handle = pyeclib_c.init(num_data, num_parity, w, ec_type) - - filename = "test_file.%s" % file_size - with open("test_files/%s" % filename, "rb") as fp: - whole_file_bytes = fp.read() - - orig_fragments = pyeclib_c.encode(handle, whole_file_bytes) - - for i in range(iterations): - fragments = orig_fragments[:] - num_missing = 1 - missing_idxs = [] - for j in range(num_missing): - idx = random.randint(0, (num_data + num_parity) - 1) - while idx in missing_idxs: - idx = random.randint(0, (num_data + num_parity) - 1) - missing_idxs.append(idx) - fragments[idx] = b'\0' * len(fragments[0]) - - timer.start() - reconstructed_fragment = pyeclib_c.reconstruct( - handle, fragments[ - :num_data], fragments[ - num_data:], missing_idxs, missing_idxs[0], len( - fragments[0])) - - tsum += timer.stop_and_return() - - if orig_fragments[missing_idxs[0]] != reconstructed_fragment: - with open("orig_fragments", "wb") as fd_orig: - fd_orig.write(orig_fragments[missing_idxs[0]]) - with open("decoded_fragments", "wb") as fd_decoded: - fd_decoded.write(reconstructed_fragment) - print(("Fragment %d was not reconstructed!!!" % missing_idxs[0])) - sys.exit(2) - - return tsum / iterations - - -def test_get_fragment_partition( - num_data, num_parity, w, ec_type, file_size, iterations): - handle = pyeclib_c.init(num_data, num_parity, w, ec_type) - - filename = "test_file.%s" % file_size - with open("test_files/%s" % filename, "rb") as fp: - whole_file_bytes = fp.read() - - fragments = pyeclib_c.encode(handle, whole_file_bytes) - - for i in range(iterations): - missing_fragments = random.sample(fragments, 3) - avail_fragments = fragments[:] - missing_fragment_idxs = [] - for missing_frag in missing_fragments: - missing_fragment_idxs.append(fragments.index(missing_frag)) - avail_fragments.remove(missing_frag) - - (data_frags, parity_frags, missing_idxs) =\ - pyeclib_c.get_fragment_partition(handle, avail_fragments) - - missing_fragment_idxs.sort() - missing_idxs.sort() - - if missing_fragment_idxs != missing_idxs: - print(("Missing idx mismatch in test_get_fragment_partition: " - "%s != %s\n" % (missing_fragment_idxs, missing_idxs))) - sys.exit() - - decoded_fragments = pyeclib_c.decode( - handle, data_frags, parity_frags, missing_idxs, len( - data_frags[0])) - - -def test_fragments_to_string(num_data, num_parity, w, ec_type, file_size): - handle = pyeclib_c.init(num_data, num_parity, w, ec_type) - - filename = "test_file.%s" % file_size - with open(("test_files/%s" % filename), "rb") as fp: - whole_file_bytes = fp.read() - - fragments = pyeclib_c.encode(handle, whole_file_bytes) - - concat_str = pyeclib_c.fragments_to_string(handle, fragments[:num_data]) - - if concat_str != whole_file_bytes: - print(("String does not equal the original string " - "(len(orig) = %d, len(new) = %d\n" % - (len(whole_file_bytes), len(concat_str)))) - - -def test_get_required_fragments(num_data, num_parity, w, ec_type): - handle = pyeclib_c.init(num_data, num_parity, w, ec_type) - - # - # MDS codes need any k fragments - # - if ec_type in ["rs_vand", "rs_cauchy_orig"]: - expected_fragments = [i for i in range(num_data + num_parity)] - missing_fragments = [] - - # - # Remove between 1 and num_parity - # - for i in range(random.randint(0, num_parity - 1)): - missing_fragment = random.sample(expected_fragments, 1)[0] - missing_fragments.append(missing_fragment) - expected_fragments.remove(missing_fragment) - - expected_fragments = expected_fragments[:num_data] - required_fragments = pyeclib_c.get_required_fragments( - handle, - missing_fragments) - - if expected_fragments != required_fragments: - print(("Unexpected required fragments list " - "(exp != req): %s != %s" % - (expected_fragments, required_fragments))) - sys.exit(2) - - -def get_throughput(avg_time, size_str): - size_desc = size_str.split("-") - - size = float(size_desc[0]) - - if (size_desc[1] == 'M'): - throughput = size / avg_time - elif (size_desc[1] == 'K'): - throughput = (size / 1000.0) / avg_time - - return (format(throughput, '.10g')) - -num_datas = [12, 12, 12] -num_parities = [2, 3, 4] -iterations = 100 - -rs_types = [("rs_vand", 16), ("rs_cauchy_orig", 4)] -xor_types = [("flat_xor_4", 12, 6, 4), ( - "flat_xor_4", 10, 5, 4), ("flat_xor_3", 10, 5, 3)] - -sizes = ["101-K", "202-K", "303-K"] - -setup(sizes) - -for (ec_type, k, m, hd) in xor_types: - print(("\nRunning tests for %s k=%d, m=%d\n" % (ec_type, k, m))) - - type_str = "%s" % (ec_type) - - for size_str in sizes: - avg_time = time_encode(k, m, 32, type_str, size_str, iterations) - print("Encode (%s): %s" % - (size_str, get_throughput(avg_time, size_str))) - - for size_str in sizes: - avg_time = time_decode(k, m, 32, type_str, size_str, iterations, 3) - print("Decode (%s): %s" % - (size_str, get_throughput(avg_time, size_str))) - - for size_str in sizes: - avg_time = test_reconstruct(k, m, 32, type_str, size_str, iterations) - print("Reconstruct (%s): %s" % - (size_str, get_throughput(avg_time, size_str))) - -for (ec_type, w) in rs_types: - print(("\nRunning tests for %s w=%d\n" % (ec_type, w))) - - for i in range(len(num_datas)): - for size_str in sizes: - test_get_fragment_partition( - num_datas[i], num_parities[i], w, ec_type, size_str, iterations) - - for i in range(len(num_datas)): - for size_str in sizes: - test_fragments_to_string( - num_datas[i], num_parities[i], w, ec_type, size_str) - - for i in range(len(num_datas)): - test_get_required_fragments(num_datas[i], num_parities[i], w, ec_type) - - for i in range(len(num_datas)): - for size_str in sizes: - avg_time = time_encode( - num_datas[i], num_parities[i], w, ec_type, size_str, iterations) - print(("Encode (%s): %s" % - (size_str, get_throughput(avg_time, size_str)))) - - for i in range(len(num_datas)): - for size_str in sizes: - avg_time = time_decode( - num_datas[i], num_parities[i], w, ec_type, size_str, iterations, - num_parities[i] + 1) - print(("Decode (%s): %s" % - (size_str, get_throughput(avg_time, size_str)))) - - for i in range(len(num_datas)): - for size_str in sizes: - avg_time = test_reconstruct( - num_datas[i], num_parities[i], w, ec_type, size_str, iterations) - print(("Reconstruct (%s): %s" % - (size_str, get_throughput(avg_time, size_str)))) - - -cleanup(sizes) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/__init__.py diff --git a/test/run_tests.py b/test/run_tests.py deleted file mode 100644 index b44e284..0000000 --- a/test/run_tests.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright (c) 2013, Kevin Greenan (kmgreen2@gmail.com) -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# Redistributions of source code must retain the above copyright notice, this -# list of conditions and the following disclaimer. -# -# Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. THIS SOFTWARE IS -# PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS -# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN -# NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -import sys -import os - -base_c_dir = "../src/c" -xor_code_dir = "%s/xor_codes" % (base_c_dir) -alg_sig_dir = "%s/alg_sig" % (base_c_dir) -pyeclib_core_dir = "%s/pyeclib" % (base_c_dir) -xor_code_test = "/usr/local/bin/test_xor_hd_code" -alg_sig_test = "/usr/local/bin/alg_sig_test" -pyeclib_core_test = "pyeclib_test.py" -pyeclib_iface_test = "ec_test.py" -base_python_dir = "../src/python" -pyeclib_core_test_dir = "%s/test" % (base_python_dir) -pyeclib_iface_test_dir = "%s/pyeclib" % (base_python_dir) -pyeclib_core_test = "pyeclib_test.py" -pyeclib_iface_test = "ec_test.py" -c_build_dirs = [(xor_code_dir, xor_code_test), (alg_sig_dir, alg_sig_test)] -py_test_dirs = [ - (pyeclib_core_test_dir, - pyeclib_core_test), - (pyeclib_iface_test_dir, - pyeclib_iface_test)] - - -def test_c_stuff(): - cur_dir = os.getcwd() - for (dir, test) in c_build_dirs: - os.system(test) - - -def pyeclib_core_test(): - cur_dir = os.getcwd() - for (dir, test) in py_test_dirs: - os.chdir(dir) - ret = os.system("python %s" % test) - if ret != 0: - print("Building %s failed!!!" % (dir)) - sys.exit(1) - os.system("rm -f *.pyc") - os.chdir(cur_dir) - - -def pyeclib_core_valgrind(): - cur_dir = os.getcwd() - for (dir, test) in py_test_dirs: - os.chdir(dir) - ret = os.system( - "valgrind --leak-check=full python %s >%s/valgrind.%s.out 2>&1" % - (test, cur_dir, test)) - if ret != 0: - print("Building %s failed!!!" % (dir)) - sys.exit(1) - os.system("rm -f *.pyc") - os.chdir(cur_dir) - - -test_c_stuff() -pyeclib_core_test() -# pyeclib_core_valgrind() diff --git a/test/test_core.py b/test/test_core.py new file mode 100644 index 0000000..b032674 --- /dev/null +++ b/test/test_core.py @@ -0,0 +1,110 @@ +# Copyright (c) 2013, Kevin Greenan (kmgreen2@gmail.com) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. THIS SOFTWARE IS +# PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +# NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import unittest + + +# +# TestCoreC Test Configuration +# +xor_code_test = "/usr/local/bin/test_xor_hd_code" +alg_sig_test = "/usr/local/bin/alg_sig_test" +c_tests = [ + xor_code_test, + alg_sig_test, +] + + +def valid_c_tests(): + """ + Asserts that the c_tests are reachable. Returns True if all of the + tests exists as a file, False otherwise. + """ + valid = True + + for test in c_tests: + if not os.path.isfile(test): + valid = False + + return valid + + +class TestCoreC(unittest.TestCase): + + def setUp(self): + pass + + def tearDown(self): + pass + + @unittest.skipUnless(valid_c_tests(), "Error locating tests in: %s" % c_tests) + def test_c_stuff(self): + for test in c_tests: + self.assertEqual(0, os.system(test)) + + +class TestCoreValgrind(unittest.TestCase): + + def __init__(self, *args): + self.pyeclib_core_test = "test_pyeclib_c.py" + self.pyeclib_iface_test = "test_pyeclib_api.py" + + unittest.TestCase.__init__(self, *args) + + def setUp(self): + # Determine which directory we're in + dirs = os.getcwd().split('/') + if dirs[-1] == 'test': + self.pyeclib_test_dir = "." + else: + self.pyeclib_test_dir = "./test" + + # Create the array of tests to run + self.py_test_dirs = [ + (self.pyeclib_test_dir, self.pyeclib_core_test), + (self.pyeclib_test_dir, self.pyeclib_iface_test) + ] + + def tearDown(self): + pass + + @unittest.skipUnless(0 == os.system("which valgrind"), "requires valgrind") + def test_core_valgrind(self): + self.assertTrue(True) + cur_dir = os.getcwd() + for (dir, test) in self.py_test_dirs: + os.chdir(dir) + if os.path.isfile(test): + ret = os.system( + "valgrind --leak-check=full python %s >%s/valgrind.%s.out 2>&1" % + (test, cur_dir, test)) + + self.assertEqual(0, ret) + os.system("rm -f *.pyc") + os.chdir(cur_dir) + else: + self.assertTrue(False) + +if __name__ == "__main__": + unittest.main() diff --git a/src/python/pyeclib/ec_test.py b/test/test_pyeclib_api.py index 73364bf..3e4f0d5 100644 --- a/src/python/pyeclib/ec_test.py +++ b/test/test_pyeclib_api.py @@ -21,15 +21,13 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -import pyeclib -from pyeclib.ec_iface import ECDriver -import unittest import random -import string +from string import ascii_letters, ascii_uppercase, digits import sys -import os +import tempfile +import unittest -from string import ascii_letters +from pyeclib.ec_iface import ECDriver if sys.version < '3': @@ -56,10 +54,7 @@ class TestNullDriver(unittest.TestCase): class TestStripeDriver(unittest.TestCase): def setUp(self): - self.stripe_driver = ECDriver( - "pyeclib.core.ECStripingDriver", - k=8, - m=0) + self.stripe_driver = ECDriver("pyeclib.core.ECStripingDriver", k=8, m=0) def tearDown(self): pass @@ -68,54 +63,51 @@ class TestStripeDriver(unittest.TestCase): class TestPyECLibDriver(unittest.TestCase): def __init__(self, *args): + # Create the temp files needed for testing self.file_sizes = ["100-K"] + self.files = {} self.num_iterations = 100 + self._create_tmp_files() unittest.TestCase.__init__(self, *args) - def setUp(self): - - if not os.path.isdir("test_files"): - os.mkdir("./test_files") - + def _create_tmp_files(self): + """ + Create the temporary files needed for testing. Use the tempfile + package so that the files will be automatically removed during + garbage collection. + """ for size_str in self.file_sizes: - + # Determine the size of the file to create size_desc = size_str.split("-") - size = int(size_desc[0]) - - if (size_desc[1] == 'M'): + if size_desc[1] == 'M': size *= 1000000 - elif (size_desc[1] == 'K'): + elif size_desc[1] == 'K': size *= 1000 - buf = ''.join(random.choice(string.ascii_letters) - for i in range(size)) + # Create the dictionary of files to test with + buf = ''.join(random.choice(ascii_letters) for i in range(size)) + tmp_file = tempfile.NamedTemporaryFile() + tmp_file.write(buf.decode('utf-8')) + self.files[size_str] = tmp_file - filename = "test_file.%s" % size_str - with open("test_files/%s" % filename, "wb") as fp: - fp.write(buf.encode('utf-8')) + def setUp(self): + # Ensure that the file offset is set to the head of the file + for _, tmp_file in self.files.items(): + tmp_file.seek(0, 0) def tearDown(self): - for size_str in self.file_sizes: - filename = "test_files/test_file.%s" % size_str - os.unlink(filename) - os.rmdir("./test_files") + pass def test_small_encode(self): pyeclib_drivers = [] - pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_vand")) - pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=11, m=2, ec_type="rs_vand")) - pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=2, ec_type="rs_vand")) + pyeclib_drivers.append(ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_vand")) + pyeclib_drivers.append(ECDriver("pyeclib.core.ECPyECLibDriver", + k=11, m=2, ec_type="rs_vand")) + pyeclib_drivers.append(ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=2, ec_type="rs_vand")) encode_strs = [b"a", b"hello", b"hellohyhi", b"yo"] @@ -129,42 +121,29 @@ class TestPyECLibDriver(unittest.TestCase): def test_verify_fragment_algsig_chksum_fail(self): pyeclib_drivers = [] pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_vand", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_vand", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=3, ec_type="rs_vand", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=3, ec_type="rs_vand", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=6, ec_type="flat_xor_4", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=6, ec_type="flat_xor_4", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=5, ec_type="flat_xor_4", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=5, ec_type="flat_xor_4", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=5, ec_type="flat_xor_3", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=5, ec_type="flat_xor_3", chksum_type="algsig")) filesize = 1024 * 1024 * 3 - - file_str = ''.join(random.choice(string.ascii_letters) - for i in range(filesize)) + file_str = ''.join(random.choice(ascii_letters) for i in range(filesize)) file_bytes = file_str.encode('utf-8') fragment_to_corrupt = random.randint(0, 12) for pyeclib_driver in pyeclib_drivers: fragments = pyeclib_driver.encode(file_bytes) - fragment_metadata_list = [] i = 0 @@ -173,49 +152,33 @@ class TestPyECLibDriver(unittest.TestCase): corrupted_fragment = fragment[:100] +\ (str(chr((b2i(fragment[100]) + 0x1) % 0xff))).encode('utf-8') + fragment[101:] - fragment_metadata_list.append( - pyeclib_driver.get_metadata(corrupted_fragment)) + fragment_metadata_list.append(pyeclib_driver.get_metadata(corrupted_fragment)) else: - fragment_metadata_list.append( - pyeclib_driver.get_metadata(fragment)) + fragment_metadata_list.append(pyeclib_driver.get_metadata(fragment)) i += 1 - self.assertTrue( - pyeclib_driver.verify_stripe_metadata(fragment_metadata_list) - != -1) + self.assertTrue(pyeclib_driver.verify_stripe_metadata(fragment_metadata_list) != -1) def test_verify_fragment_inline_succeed(self): pyeclib_drivers = [] pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_vand", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_vand", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=3, ec_type="rs_vand", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=3, ec_type="rs_vand", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=6, ec_type="flat_xor_4", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=6, ec_type="flat_xor_4", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=5, ec_type="flat_xor_4", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=5, ec_type="flat_xor_4", chksum_type="algsig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=5, ec_type="flat_xor_3", - chksum_type="algsig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=5, ec_type="flat_xor_3", chksum_type="algsig")) filesize = 1024 * 1024 * 3 - - file_str = ''.join(random.choice(string.ascii_letters) - for i in range(filesize)) + file_str = ''.join(random.choice(ascii_letters) for i in range(filesize)) file_bytes = file_str.encode('utf-8') for pyeclib_driver in pyeclib_drivers: @@ -228,36 +191,25 @@ class TestPyECLibDriver(unittest.TestCase): pyeclib_driver.get_metadata(fragment)) self.assertTrue( - pyeclib_driver.verify_stripe_metadata(fragment_metadata_list) - == -1) + pyeclib_driver.verify_stripe_metadata(fragment_metadata_list) == -1) def test_verify_fragment_inline_chksum_fail(self): pyeclib_drivers = [] pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_vand", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_vand", chksum_type="inline")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=3, ec_type="rs_vand", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=3, ec_type="rs_vand", chksum_type="inline")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=4, ec_type="rs_vand", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=4, ec_type="rs_vand", chksum_type="inline")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_cauchy_orig", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_cauchy_orig", chksum_type="inline")) filesize = 1024 * 1024 * 3 - - file_str = ''.join(random.choice(string.ascii_letters) - for i in range(filesize)) + file_str = ''.join(random.choice(ascii_letters) for i in range(filesize)) file_bytes = file_str.encode('utf-8') fragment_to_corrupt = random.randint(0, 12) @@ -280,36 +232,27 @@ class TestPyECLibDriver(unittest.TestCase): pyeclib_driver.get_metadata(fragment)) i += 1 - self.assertTrue(pyeclib_driver.verify_stripe_metadata( - fragment_metadata_list) == fragment_to_corrupt) + self.assertEqual( + pyeclib_driver.verify_stripe_metadata(fragment_metadata_list), + fragment_to_corrupt) def test_verify_fragment_inline_chksum_succeed(self): pyeclib_drivers = [] pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_vand", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_vand", chksum_type="inline")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=3, ec_type="rs_vand", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=3, ec_type="rs_vand", chksum_type="inline")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=4, ec_type="rs_vand", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=4, ec_type="rs_vand", chksum_type="inline")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_cauchy_orig", - chksum_type="inline")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_cauchy_orig", chksum_type="inline")) filesize = 1024 * 1024 * 3 - - file_str = ''.join(random.choice(string.ascii_letters) - for i in range(filesize)) + file_str = ''.join(random.choice(ascii_letters) for i in range(filesize)) file_bytes = file_str.encode('utf-8') for pyeclib_driver in pyeclib_drivers: @@ -322,23 +265,19 @@ class TestPyECLibDriver(unittest.TestCase): pyeclib_driver.get_metadata(fragment)) self.assertTrue( - pyeclib_driver.verify_stripe_metadata(fragment_metadata_list) - == -1) + pyeclib_driver.verify_stripe_metadata(fragment_metadata_list) == -1) def test_get_segment_info(self): pyeclib_drivers = [] pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_vand")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_vand")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=11, m=2, ec_type="rs_vand")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=11, m=2, ec_type="rs_vand")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=2, ec_type="rs_vand")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=2, ec_type="rs_vand")) file_sizes = [ 1024 * 1024, @@ -353,12 +292,10 @@ class TestPyECLibDriver(unittest.TestCase): # Use 2 * segment size, because last segment may be # greater than segment_size # - char_set = string.ascii_uppercase + string.digits + char_set = ascii_uppercase + digits for segment_size in segment_sizes: - segment_strings[segment_size] = ''.join( - random.choice(char_set) for i in range( - segment_size * - 2)) + segment_strings[segment_size] = \ + ''.join(random.choice(char_set) for i in range(segment_size * 2)) for pyeclib_driver in pyeclib_drivers: for file_size in file_sizes: @@ -408,44 +345,35 @@ class TestPyECLibDriver(unittest.TestCase): def test_rs(self): pyeclib_drivers = [] pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_vand")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_vand")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=2, ec_type="rs_cauchy_orig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=2, ec_type="rs_cauchy_orig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=3, ec_type="rs_vand")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=3, ec_type="rs_vand")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=3, ec_type="rs_cauchy_orig")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=3, ec_type="rs_cauchy_orig")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=12, m=6, ec_type="flat_xor_4")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=12, m=6, ec_type="flat_xor_4")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=5, ec_type="flat_xor_4")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=5, ec_type="flat_xor_4")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=10, m=5, ec_type="flat_xor_3")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=10, m=5, ec_type="flat_xor_3")) pyeclib_drivers.append( - ECDriver( - "pyeclib.core.ECPyECLibDriver", - k=9, m=5, ec_type="flat_xor_3")) + ECDriver("pyeclib.core.ECPyECLibDriver", + k=9, m=5, ec_type="flat_xor_3")) for pyeclib_driver in pyeclib_drivers: for file_size in self.file_sizes: - - filename = "test_file.%s" % file_size - with open("test_files/%s" % filename, "r") as fp: - whole_file_str = fp.read() + tmp_file = self.files[file_size] + tmp_file.seek(0) + whole_file_str = tmp_file.read() whole_file_bytes = whole_file_str.encode('utf-8') encode_input = whole_file_bytes @@ -485,5 +413,6 @@ class TestPyECLibDriver(unittest.TestCase): reconstructed_fragments[0] == orig_fragments[ idxs_to_remove[0]]) + if __name__ == '__main__': unittest.main() diff --git a/test/test_pyeclib_c.py b/test/test_pyeclib_c.py new file mode 100644 index 0000000..5539efd --- /dev/null +++ b/test/test_pyeclib_c.py @@ -0,0 +1,419 @@ +# Copyright (c) 2013, Kevin Greenan (kmgreen2@gmail.com) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. THIS SOFTWARE IS +# PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +# NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY +# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import random +from string import ascii_letters +import tempfile +import time +import unittest + +import pyeclib_c + + +class Timer: + + def __init__(self): + self.start_time = 0 + self.end_time = 0 + + def start(self): + self.start_time = time.time() + + def stop(self): + self.end_time = time.time() + + def curr_delta(self): + return self.end_time - self.start_time + + def stop_and_return(self): + self.end_time = time.time() + return self.curr_delta() + + +class TestPyECLib(unittest.TestCase): + + def __init__(self, *args): + self.num_datas = [12, 12, 12] + self.num_parities = [2, 3, 4] + self.iterations = 100 + + # EC algorithm and config parameters + self.rs_types = [("rs_vand", 16), ("rs_cauchy_orig", 4)] + self.xor_types = [("flat_xor_4", 12, 6, 4), + ("flat_xor_4", 10, 5, 4), + ("flat_xor_3", 10, 5, 3)] + + # Input temp files for testing + self.sizes = ["101-K", "202-K", "303-K"] + self.files = {} + self._create_tmp_files() + + unittest.TestCase.__init__(self, *args) + + def _create_tmp_files(self): + """ + Create the temporary files needed for testing. Use the tempfile + package so that the files will be automatically removed during + garbage collection. + """ + for size_str in self.sizes: + # Determine the size of the file to create + size_desc = size_str.split("-") + size = int(size_desc[0]) + if size_desc[1] == 'M': + size *= 1000000 + elif size_desc[1] == 'K': + size *= 1000 + + # Create the dictionary of files to test with + buf = ''.join(random.choice(ascii_letters) for i in range(size)) + tmp_file = tempfile.NamedTemporaryFile() + tmp_file.write(buf.decode('utf-8')) + self.files[size_str] = tmp_file + + def get_tmp_file(self, name): + """ + Acquire a temp file from the dictionary of pre-built, random files + with the seek position to the head of the file. + """ + tmp_file = self.files.get(name, None) + + if tmp_file: + tmp_file.seek(0, 0) + + return tmp_file + + def setUp(self): + # Ensure that the file offset is set to the head of the file + for _, tmp_file in self.files.items(): + tmp_file.seek(0, 0) + + def tearDown(self): + pass + + def time_encode(self, num_data, num_parity, w, ec_type, file_size, iterations): + """ + :return average encode time + """ + timer = Timer() + tsum = 0 + handle = pyeclib_c.init(num_data, num_parity, w, ec_type) + whole_file_bytes = self.get_tmp_file(file_size).read() + + timer.start() + for l in range(iterations): + fragments = pyeclib_c.encode(handle, whole_file_bytes) + tsum = timer.stop_and_return() + + return tsum / iterations + + def time_decode(self, + num_data, num_parity, w, ec_type, + file_size, iterations, hd): + """ + :return 2-tuple, (success, average decode time) + """ + timer = Timer() + tsum = 0 + handle = pyeclib_c.init(num_data, num_parity, w, ec_type) + whole_file_bytes = self.get_tmp_file(file_size).read() + success = True + + fragments = pyeclib_c.encode(handle, whole_file_bytes) + orig_fragments = fragments[:] + + for i in range(iterations): + missing_idxs = [] + num_missing = hd - 1 + for j in range(num_missing): + idx = random.randint(0, (num_data + num_parity) - 1) + while idx in missing_idxs: + idx = random.randint(0, (num_data + num_parity) - 1) + missing_idxs.append(idx) + fragments[idx] = b'\0' * len(fragments[0]) + + timer.start() + decoded_fragments = pyeclib_c.decode(handle, + fragments[:num_data], + fragments[num_data:], + missing_idxs, + len(fragments[0])) + tsum += timer.stop_and_return() + + fragments = decoded_fragments + + for j in range(num_data + num_parity): + if orig_fragments[j] != decoded_fragments[j]: + success = False + + # Output the fragments for debugging + #with open("orig_fragments", "wb") as fd_orig: + # fd_orig.write(orig_fragments[j]) + #with open("decoded_fragments", "wb") as fd_decoded: + # fd_decoded.write(decoded_fragments[j]) + #print(("Fragment %d was not reconstructed!!!" % j)) + #sys.exit(2) + + decoded_fragments = None + + return success, tsum / iterations + + def time_reconstruct(self, + num_data, num_parity, w, ec_type, + file_size, iterations): + """ + :return 2-tuple, (success, average reconstruct time) + """ + timer = Timer() + tsum = 0 + handle = pyeclib_c.init(num_data, num_parity, w, ec_type) + whole_file_bytes = self.get_tmp_file(file_size).read() + success = True + + orig_fragments = pyeclib_c.encode(handle, whole_file_bytes) + + for i in range(iterations): + fragments = orig_fragments[:] + num_missing = 1 + missing_idxs = [] + for j in range(num_missing): + idx = random.randint(0, (num_data + num_parity) - 1) + while idx in missing_idxs: + idx = random.randint(0, (num_data + num_parity) - 1) + missing_idxs.append(idx) + fragments[idx] = b'\0' * len(fragments[0]) + + timer.start() + reconstructed_fragment = pyeclib_c.reconstruct(handle, + fragments[:num_data], + fragments[num_data:], + missing_idxs, + missing_idxs[0], + len(fragments[0])) + tsum += timer.stop_and_return() + + if orig_fragments[missing_idxs[0]] != reconstructed_fragment: + success = False + # Output the fragments for debugging + #with open("orig_fragments", "wb") as fd_orig: + # fd_orig.write(orig_fragments[missing_idxs[0]]) + #with open("decoded_fragments", "wb") as fd_decoded: + # fd_decoded.write(reconstructed_fragment) + #print(("Fragment %d was not reconstructed!!!" % missing_idxs[0])) + #sys.exit(2) + + return success, tsum / iterations + + def get_throughput(self, avg_time, size_str): + size_desc = size_str.split("-") + size = float(size_desc[0]) + + if size_desc[1] == 'M': + throughput = size / avg_time + elif size_desc[1] == 'K': + throughput = (size / 1000.0) / avg_time + + return format(throughput, '.10g') + + def test_xor_code(self): + for (ec_type, k, m, hd) in self.xor_types: + print(("\nRunning tests for %s k=%d, m=%d" % (ec_type, k, m))) + + type_str = "%s" % (ec_type) + + for size_str in self.sizes: + avg_time = self.time_encode(k, m, 32, + type_str, size_str, + self.iterations) + print("Encode (%s): %s" % + (size_str, self.get_throughput(avg_time, size_str))) + + for size_str in self.sizes: + success, avg_time = self.time_decode(k, m, 32, + type_str, size_str, + self.iterations, 3) + self.assertTrue(success) + print("Decode (%s): %s" % + (size_str, self.get_throughput(avg_time, size_str))) + + for size_str in self.sizes: + success, avg_time = self.time_reconstruct(k, m, 32, + type_str, size_str, + self.iterations) + self.assertTrue(success) + print("Reconstruct (%s): %s" % + (size_str, self.get_throughput(avg_time, size_str))) + + def _test_get_fragment_partition(self, + num_data, num_parity, w, ec_type, + file_size, iterations): + """ + :return boolean, True if all tests passed + """ + handle = pyeclib_c.init(num_data, num_parity, w, ec_type) + whole_file_bytes = self.get_tmp_file(file_size).read() + success = True + + fragments = pyeclib_c.encode(handle, whole_file_bytes) + + for i in range(iterations): + missing_fragments = random.sample(fragments, 3) + avail_fragments = fragments[:] + missing_fragment_idxs = [] + for missing_frag in missing_fragments: + missing_fragment_idxs.append(fragments.index(missing_frag)) + avail_fragments.remove(missing_frag) + + (data_frags, parity_frags, missing_idxs) =\ + pyeclib_c.get_fragment_partition(handle, avail_fragments) + + missing_fragment_idxs.sort() + missing_idxs.sort() + + if missing_fragment_idxs != missing_idxs: + success = False + print(("Missing idx mismatch in test_get_fragment_partition: " + "%s != %s\n" % (missing_fragment_idxs, missing_idxs))) + #sys.exit() + + decoded_frags = pyeclib_c.decode(handle, data_frags, parity_frags, + missing_idxs, len(data_frags[0])) + + return success + + def _test_fragments_to_string(self, + num_data, num_parity, w, ec_type, + file_size): + """ + :return boolean, True if all tests passed + """ + handle = pyeclib_c.init(num_data, num_parity, w, ec_type) + whole_file_bytes = self.get_tmp_file(file_size).read() + success = True + + fragments = pyeclib_c.encode(handle, whole_file_bytes) + concat_str = pyeclib_c.fragments_to_string(handle, fragments[:num_data]) + + if concat_str != whole_file_bytes: + success = False + print(("String does not equal the original string " + "(len(orig) = %d, len(new) = %d\n" % + (len(whole_file_bytes), len(concat_str)))) + + return success + + def _test_get_required_fragments(self, num_data, num_parity, w, ec_type): + """ + :return boolean, True if all tests passed + """ + handle = pyeclib_c.init(num_data, num_parity, w, ec_type) + success = True + + # + # MDS codes need any k fragments + # + if ec_type in ["rs_vand", "rs_cauchy_orig"]: + expected_fragments = [i for i in range(num_data + num_parity)] + missing_fragments = [] + + # + # Remove between 1 and num_parity + # + for i in range(random.randint(0, num_parity - 1)): + missing_fragment = random.sample(expected_fragments, 1)[0] + missing_fragments.append(missing_fragment) + expected_fragments.remove(missing_fragment) + + expected_fragments = expected_fragments[:num_data] + required_fragments = pyeclib_c.get_required_fragments( + handle, + missing_fragments) + + if expected_fragments != required_fragments: + success = False + print(("Unexpected required fragments list " + "(exp != req): %s != %s" % + (expected_fragments, required_fragments))) + #sys.exit(2) + + return success + + def test_rs_code(self): + for (ec_type, w) in self.rs_types: + print(("\nRunning tests for %s w=%d" % (ec_type, w))) + + for i in range(len(self.num_datas)): + for size_str in self.sizes: + success = self._test_get_fragment_partition(self.num_datas[i], + self.num_parities[i], + w, ec_type, size_str, + self.iterations) + self.assertTrue(success) + + for i in range(len(self.num_datas)): + for size_str in self.sizes: + success = self._test_fragments_to_string(self.num_datas[i], + self.num_parities[i], + w, ec_type, size_str) + self.assertTrue(success) + + for i in range(len(self.num_datas)): + success = self._test_get_required_fragments(self.num_datas[i], + self.num_parities[i], + w, ec_type) + self.assertTrue(success) + + for i in range(len(self.num_datas)): + for size_str in self.sizes: + avg_time = self.time_encode(self.num_datas[i], + self.num_parities[i], + w, ec_type, size_str, + self.iterations) + print(("Encode (%s): %s" % + (size_str, self.get_throughput(avg_time, size_str)))) + + for i in range(len(self.num_datas)): + for size_str in self.sizes: + success, avg_time = self.time_decode(self.num_datas[i], + self.num_parities[i], + w, ec_type, size_str, + self.iterations, + self.num_parities[i] + 1) + self.assertTrue(success) + print(("Decode (%s): %s" % + (size_str, self.get_throughput(avg_time, size_str)))) + + for i in range(len(self.num_datas)): + for size_str in self.sizes: + success, avg_time = self.time_reconstruct(self.num_datas[i], + self.num_parities[i], + w, ec_type, size_str, + self.iterations) + self.assertTrue(success) + print(("Reconstruct (%s): %s" % + (size_str, self.get_throughput(avg_time, size_str)))) + + +if __name__ == "__main__": + unittest.main() + |