summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Greenan <kmgreen2@gmail.com>2014-06-13 12:25:23 -0700
committerKevin Greenan <kmgreen2@gmail.com>2014-06-13 12:25:23 -0700
commit200fba20f711fc4f2d28661e64d95462ce6f9500 (patch)
tree3fc56aed96f7011dc569661c19332e3e8671ac82
parent0f7f2b3dcf4f3ceb4ad9acebfbd4baf61836b6d1 (diff)
parenta1332c135ebaa2f168a256b0a92dc0b9f654f57f (diff)
downloadpyeclib-200fba20f711fc4f2d28661e64d95462ce6f9500.tar.gz
Merged in issue-44 (pull request #8)
Unit test refactor
-rw-r--r--setup.py3
-rw-r--r--src/python/test/pyeclib_test.py356
-rw-r--r--test/__init__.py0
-rw-r--r--test/run_tests.py82
-rw-r--r--test/test_core.py110
-rw-r--r--test/test_pyeclib_api.py (renamed from src/python/pyeclib/ec_test.py)291
-rw-r--r--test/test_pyeclib_c.py419
7 files changed, 641 insertions, 620 deletions
diff --git a/setup.py b/setup.py
index 9ab0bdc..6a1e965 100644
--- a/setup.py
+++ b/setup.py
@@ -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()
+