Esempio n. 1
0
class TestMerkleLeaf(unittest.TestCase):
    """ Test MerkleLeaf functionality. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################

    # actual unit tests #############################################
    def do_test_simple_constructor(self, hashtype):
        """ Test constructor for specific SHA type. """

        check_hashtype(hashtype)
        # pylint: disable=redefined-variable-type
        if hashtype == HashTypes.SHA1:
            sha = hashlib.sha1()
        elif hashtype == HashTypes.SHA2:
            sha = hashlib.sha256()
        elif hashtype == HashTypes.SHA3:
            sha = hashlib.sha3_256()

        file_name = self.rng.next_file_name(8)
        nnn = self.rng.some_bytes(8)
        sha.update(nnn)
        hash0 = sha.digest()

        leaf0 = MerkleLeaf(file_name, hashtype, hash0)
        self.assertEqual(file_name, leaf0.name)
        self.assertEqual(hash0, leaf0.bin_hash)

        file_name2 = file_name
        while file_name2 == file_name:
            file_name2 = self.rng.next_file_name(8)
        nnn = self.rng.some_bytes(8)
        self.rng.next_bytes(nnn)
        sha.update(nnn)
        hash1 = sha.digest()
        leaf1 = MerkleLeaf(file_name2, hashtype, hash1)
        self.assertEqual(file_name2, leaf1.name)
        self.assertEqual(hash1, leaf1.bin_hash)

        self.assertTrue(leaf0.equal(leaf0))
        self.assertFalse(leaf0.equal(leaf1))

        # XXX USE NLHTree instead
        #pair0    = leaf0.toPair()
        #leaf0bis = MerkleLeaf.createFromPair(pair0)
        #self.assertEqual(leaf0bis, leaf0)

        #pair1    = leaf1.toPair()
        #leaf1bis = MerkleLeaf.createFromPair(pair1)
        #self.assertEqual(leaf1bis, leaf1)

    def test_simple_constructor(self):
        """ Test constructor for various SHA types. """
        for hashtype in HashTypes:
            self.do_test_simple_constructor(hashtype=hashtype)
Esempio n. 2
0
class TestMerkleTree2(unittest.TestCase):
    """ Test MerkleTree behavior with deeper directories. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def do_test_deepish_trees(self, hashtype):
        """
        Build a directory of random data, then its MerkleTree, then
        round trip to a serialization and back.
        """

        tree_top = os.path.join('tmp', self.rng.next_file_name(MAX_NAME_LEN))
        while os.path.exists(tree_top):
            tree_top = os.path.join(
                'tmp', self.rng.next_file_name(MAX_NAME_LEN))

        # Generate a quasi-random data directory, 7 deep, up to 5 files/dir
        self.rng.next_data_dir(tree_top, depth=7, width=5, max_len=4096)

        # Build a MerkleTree specifying the directory.
        tree = MerkleTree.create_from_file_system(tree_top, hashtype)

        # ROUND TRIP 1 ----------------------------------------------

        # Serialize it.
        ser = tree.__str__()

        # Deserialize to make another MerkleTree.
        tree2 = MerkleTree.create_from_serialization(ser, hashtype)

        self.assertTrue(tree2.__eq__(tree))
        self.assertEqual(tree2, tree)           # identical test

        # ROUND TRIP 2 ----------------------------------------------
        strings = ser.split('\n')
        strings = strings[:-1]
        tree3 = MerkleTree.create_from_string_array(strings, hashtype)
        self.assertEqual(tree3, tree)

        # ROUND TRIP 3 ----------------------------------------------
        filename = os.path.join('tmp', self.rng.next_file_name(8))
        while os.path.exists(filename):
            filename = os.path.join('tmp', self.rng.next_file_name(8))
        with open(filename, 'w') as file:
            file.write(ser)

        tree4 = MerkleTree.create_from_file(filename, hashtype)
        self.assertEqual(tree4, tree)

    def test_deepish_trees(self):
        """ Test behavior of deeper trees using various SHA hash types. """

        for hashtype in HashTypes:
            self.do_test_deepish_trees(hashtype)
Esempio n. 3
0
class TestMerkleTree2(unittest.TestCase):
    """ Test MerkleTree behavior with deeper directories. """
    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def do_test_deepish_trees(self, hashtype):
        """
        Build a directory of random data, then its MerkleTree, then
        round trip to a serialization and back.
        """

        tree_top = os.path.join('tmp', self.rng.next_file_name(MAX_NAME_LEN))
        while os.path.exists(tree_top):
            tree_top = os.path.join('tmp',
                                    self.rng.next_file_name(MAX_NAME_LEN))

        # Generate a quasi-random data directory, 7 deep, up to 5 files/dir
        self.rng.next_data_dir(tree_top, depth=7, width=5, max_len=4096)

        # Build a MerkleTree specifying the directory.
        tree = MerkleTree.create_from_file_system(tree_top, hashtype)

        # ROUND TRIP 1 ----------------------------------------------

        # Serialize it.
        ser = tree.__str__()

        # Deserialize to make another MerkleTree.
        tree2 = MerkleTree.create_from_serialization(ser, hashtype)

        self.assertTrue(tree2.__eq__(tree))
        self.assertEqual(tree2, tree)  # identical test

        # ROUND TRIP 2 ----------------------------------------------
        strings = ser.split('\n')
        strings = strings[:-1]
        tree3 = MerkleTree.create_from_string_array(strings, hashtype)
        self.assertEqual(tree3, tree)

        # ROUND TRIP 3 ----------------------------------------------
        filename = os.path.join('tmp', self.rng.next_file_name(8))
        while os.path.exists(filename):
            filename = os.path.join('tmp', self.rng.next_file_name(8))
        with open(filename, 'w') as file:
            file.write(ser)

        tree4 = MerkleTree.create_from_file(filename, hashtype)
        self.assertEqual(tree4, tree)

    def test_deepish_trees(self):
        """ Test behavior of deeper trees using various SHA hash types. """

        for hashtype in HashTypes:
            self.do_test_deepish_trees(hashtype)
Esempio n. 4
0
class TestBuildList(unittest.TestCase):
    """ Test BuildList.listgen functionality. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def do_listgen_test(self, title, hashtype, dirstruc):
        """
        Test buildlist functionality for specific hash type and DirStruc.
        """

        # MAJOR ERROR: This code logs to .dvcz/buildlist, the actual
        # project log!  Fix is:
        dvcz_dir = os.path.join('tmp', self.rng.next_file_name(8))
        while os.path.exists(dvcz_dir):
            dvcz_dir = os.path.join('tmp', self.rng.next_file_name(8))
        os.mkdir(dvcz_dir, 0o744)

        # create the BuildList from what's in DATA_DIR
        # -- RESTRUCTURE and just do this once for each hashtype -- in
        #    other words, this should be in a higher level function, one
        #    which runs a test for each dirstruc
        BuildList.list_gen(
            title=title,
            data_dir=DATA_DIR,
            dvcz_dir=dvcz_dir,  # THE FIX
            # list_file=        # lastBuildList
            logging=True,
            u_path=os.path.join('tmp', str(hashtype.value), dirstruc.name),
            hashtype=hashtype,
            using_indir=True
        )

        # THE SAME BUILDLIST IS USED FOR EACH OF THE THREE DIRSTRUCS
        # UNFINISHED

        # Compare the BuildList with
        # UNFINISHED

    def test_build_list(self):
        """ Test listgen functionality for suppored hash types. """

        # DEBUG
        # print("DATA_DIR is '%s'" % DATA_DIR)
        # END
        self.assertTrue(os.path.exists(DATA_DIR))
        self.assertTrue(os.path.exists(RSA_FILE))

        for hashtype in HashTypes:
            for dirstruc in DirStruc:
                self.do_listgen_test('SHA test', hashtype, dirstruc)
Esempio n. 5
0
class TestTimestamp(unittest.TestCase):
    """ Ostensibly tests BuildList timestamp.  (Why?) """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def test_sha1_file(self):
        """
        Verify functioning of xlu.file_sha1hex().
        """

        blk_count = 1 + self.rng.next_int16(3)     # so 1 to 3
        # last block will usually be only partically populated
        byte_count = BuildList.BLOCK_SIZE * (blk_count - 1) +\
            self.rng.next_int16(BuildList.BLOCK_SIZE)

        data = bytearray(byte_count)        # that many null bytes
        self.rng.next_bytes(data)           # fill with random data
        d_val = hashlib.new('sha1')
        d_val.update(data)
        hash_ = d_val.hexdigest()

        # make a unique test file name
        file_name = self.rng.next_file_name(8)
        path_to_file = os.path.join('tmp', file_name)
        while os.path.exists(path_to_file):
            file_name = self.rng.next_file_name(8)
            path_to_file = os.path.join('tmp', file_name)

        with open(path_to_file, 'wb') as file:
            file.write(data)

        file_hash = file_sha1hex(path_to_file)

        self.assertEqual(hash_, file_hash)
Esempio n. 6
0
class TestNLHBase(unittest.TestCase):
    """ Test basic NLHTree functions. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def do_test_constructor(self, hashtype):
        """ Check functionality of NLHBase constructor for specifc hash. """

        name = self.rng.next_file_name(8)
        base = NLHBase(name, hashtype)
        self.assertEqual(base.name, name)
        self.assertEqual(base.hashtype, hashtype)
        root = base.root
        curt = base.cur_tree
        self.assertEqual(root.name, curt.name)

    def test_constructor(self):
        """ Check functionality of NLHBase constructor.  """

        for hashtype in HashTypes:
            self.do_test_constructor(hashtype)

    def do_test_with_simple_tree(self, hashtype):
        """ XXX STUB: test simple tree with specific hash. """

        if hashtype == HashTypes.SHA1:
            sha = hashlib.sha1()
        elif hashtype == HashTypes.SHA2:
            sha = hashlib.sha256()
        elif hashtype == HashTypes.SHA3:
            # pylint:disable=no-member
            sha = hashlib.sha3_256()
        elif hashtype == HashTypes.BLAKE2B:
            sha = hashlib.blake2b(digest_size=32)
        else:
            raise NotImplementedError

        assert sha          # suppress warning

    def test_simple_tree(self):
        """ XXX STUB: test building simple tree. """
        for hashtype in HashTypes:
            self.do_test_with_simple_tree(hashtype)
Esempio n. 7
0
class TestRSA(unittest.TestCase):
    """ Test RSA crypto routines.  """
    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def test_rsa_serialization(self):
        """
        Exercise basic RSA functions.

        These include key generation, public key extraction,
        serialization/deserialization for pem and der formats, and
        digital signing and verification.
        """

        # ignore warning about renaming internal to cryptography
        warnings.filterwarnings("ignore", category=PendingDeprecationWarning)

        tmp_dir = 'tmp'
        os.makedirs(tmp_dir, exist_ok=True, mode=0o755)
        while True:
            sub_dir = self.rng.next_file_name(12)
            node_dir = os.path.join(tmp_dir, sub_dir)
            if not os.path.exists(node_dir):
                break
        # DEBUG
        print("node_dir is %s" % node_dir)
        # END
        os.mkdir(node_dir, mode=0o755)

        # RSA PRIVATE KEY GENERATION -----------------------------

        sk_priv = rsa.generate_private_key(
            public_exponent=65537,
            key_size=1024,  # cheap key for testing
            backend=default_backend())
        sk_ = sk_priv.public_key()

        self.assertEqual(sk_priv.key_size, 1024)

        # PEM FORMAT RSA PRIVATE KEY ROUND-TRIPPED ------------------

        pem = sk_priv.private_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption())

        key_file = os.path.join(node_dir, 'skPriv.pem')
        with open(key_file, 'wb') as file:
            # written as bytes
            file.write(pem)

        self.assertTrue(os.path.exists(key_file))
        with open(key_file, 'rb') as file:
            sk2_priv = serialization.load_pem_private_key(
                file.read(), password=None, backend=default_backend())

        # NUMBERS AND KEY EQUALITY ----------------------------------

        # get the public part of the key
        sk2_ = sk2_priv.public_key()

        # __eq__() for public part of RSA keys -------------

        # FAILS because __eq__()  has not been defined
        # self.assertEqual(sk2_, sk_)

        def check_equal_rsa_pub_key(sk2_, sk_):
            """  __eq__ functionalitiy for RSA public keys. """
            pub_n = sk_.public_numbers()
            pub_n2 = sk2_.public_numbers()

            self.assertEqual(pub_n2.e, pub_n.e)
            self.assertEqual(pub_n2.n, pub_n.n)

        check_equal_rsa_pub_key(sk2_, sk_)

        def check_equal_rsa_priv_key(sk2_priv, sk_priv):
            """  __eq__ functionalitiy for RSA private keys. """
            pri_n = sk_priv.private_numbers()
            pri_n2 = sk2_priv.private_numbers()

            # the library guarantees this: p is the larger factor
            self.assertTrue(pri_n.p > pri_n.q)

            self.assertTrue(pri_n2.p == pri_n.p and pri_n2.q == pri_n.q
                            and pri_n2.d == pri_n.d
                            and pri_n2.dmp1 == pri_n.dmp1
                            and pri_n2.dmq1 == pri_n.dmq1
                            and pri_n2.iqmp == pri_n.iqmp)

        check_equal_rsa_priv_key(sk2_priv, sk_priv)

        # DER DE/SERIALIZATION ROUND-TRIPPED ------------------------

        der = sk_priv.private_bytes(
            encoding=serialization.Encoding.DER,
            format=serialization.PrivateFormat.PKCS8,
            encryption_algorithm=serialization.NoEncryption())

        der_key_file = os.path.join(node_dir, 'skPriv.der')
        with open(der_key_file, 'wb') as file:
            # written as bytes
            file.write(der)

        self.assertTrue(os.path.exists(der_key_file))
        with open(der_key_file, 'rb') as file:
            sk3_priv = serialization.load_der_private_key(
                file.read(), password=None, backend=default_backend())

        check_equal_rsa_priv_key(sk3_priv, sk_priv)

        # OpenSSH PUBLIC KEY DE/SERIALIZATION ROUND-TRIPPED ---------

        ssh_bytes = sk_.public_bytes(encoding=serialization.Encoding.OpenSSH,
                                     format=serialization.PublicFormat.OpenSSH)

        ssh_key_file = os.path.join(node_dir, 'sk.ssh')
        with open(ssh_key_file, 'wb') as file:
            # written as bytes
            file.write(ssh_bytes)

        self.assertTrue(os.path.exists(ssh_key_file))
        with open(ssh_key_file, 'rb') as file:
            sk4_ = serialization.load_ssh_public_key(file.read(),
                                                     backend=default_backend())

        check_equal_rsa_pub_key(sk4_, sk_)  # GEEP 175

        # PEM FORMAT RSA PUBLIC KEY ROUND-TRIPPED -------------------

        pem = sk_.public_bytes(encoding=serialization.Encoding.PEM,
                               format=serialization.PublicFormat.PKCS1)

        key_file = os.path.join(node_dir, 'sk.pem')
        with open(key_file, 'wb') as file:
            # written as bytes
            file.write(pem)

        self.assertTrue(os.path.exists(key_file))
        with open(key_file, 'rb') as file:
            sk5_ = serialization.load_pem_public_key(
                file.read(), backend=default_backend())  # GEEP 193

        check_equal_rsa_pub_key(sk5_, sk_)

    def test_dig_sig(self):
        """ Test digital signatures using a range of hash types. """

        for using in [
                HashTypes.SHA1,
                HashTypes.SHA2,
        ]:
            self.do_test_dig_sig(using)

    def do_test_dig_sig(self, hashtype):
        """"
        Verify calculation of digital signature using speciic hash type.
        """

        if hashtype == HashTypes.SHA1:
            sha = hashes.SHA1
        elif hashtype == HashTypes.SHA2:
            sha = hashes.SHA256
        sk_priv = rsa.generate_private_key(
            public_exponent=65537,
            key_size=1024,  # cheap key for testing
            backend=default_backend())
        sk_ = sk_priv.public_key()

        print("WARNING: cannot use hashlib's sha code with pyca cryptography")
        print("WARNING: pyca cryptography does not support sha3/keccak")

        signer = sk_priv.signer(
            padding.PSS(mgf=padding.MGF1(sha()),
                        salt_length=padding.PSS.MAX_LENGTH), sha())

        count = 64 + self.rng.next_int16(192)  # [64..256)
        data = bytes(self.rng.some_bytes(count))

        signer.update(data)
        signature = signer.finalize()  # a binary value; bytes

        # BEGIN interlude: conversion to/from base64, w/ 76-byte lines
        b64sig = base64.encodebytes(signature).decode('utf-8')
        sig2 = base64.decodebytes(b64sig.encode('utf-8'))
        self.assertEqual(sig2, signature)
        # END interlude ---------------------------------------------

        verifier = sk_.verifier(
            signature,
            padding.PSS(mgf=padding.MGF1(sha()),
                        salt_length=padding.PSS.MAX_LENGTH), sha())
        verifier.update(data)

        try:
            verifier.verify()
            # digital signature verification succeeded
        except InvalidSignature:
            self.fail("dig sig verification unexpectedly failed")

        # twiddle a random byte in data array to make verification fail
        data2 = bytearray(data)
        which = self.rng.next_int16(count)
        data2[which] = 0xff & ~data2[which]
        data3 = bytes(data2)

        verifier = sk_.verifier(
            signature,  # same digital signature
            padding.PSS(mgf=padding.MGF1(sha()),
                        salt_length=padding.PSS.MAX_LENGTH),
            sha())
        verifier.update(data3)

        try:
            verifier.verify()
            self.fail("expected verification of modified message to fail")

        except InvalidSignature:
            pass  # digital signature verification failed
Esempio n. 8
0
class TestRandomDir(unittest.TestCase):
    """ Test building quasi-random data files and directory structures. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################

    # actual unit tests #############################################

    def do_test_random_dir(self, hashtype):
        """ Test building random directories with specific SHA hash type. """
        check_hashtype(hashtype)
        depth = 1 + self.rng.next_int16(3)       # so 1 to 3
        width = 1 + self.rng.next_int16(16)      # so 1 to 16

        blk_count = 1 + self.rng.next_int16(3)     # so 1 to 3
        # last block will usually be only partically populated
        max_len = BuildList.BLOCK_SIZE * (blk_count - 1) +\
            self.rng.next_int16(BuildList.BLOCK_SIZE)
        min_len = 1

        # we want the directory name to be unique
        path_to_dir = os.path.join('tmp', self.rng.next_file_name(8))
        while os.path.exists(path_to_dir):
            path_to_dir = os.path.join('tmp', self.rng.next_file_name(8))

        self.rng.next_data_dir(path_to_dir, depth, width, max_len, min_len)

        data = bytearray(max_len)            # that many null bytes
        self.rng.next_bytes(data)            # fill with random data
        if hashtype == HashTypes.SHA1:
            sha = hashlib.sha1()
        elif hashtype == HashTypes.SHA2:
            sha = hashlib.sha256()
        elif hashtype == HashTypes.SHA3:
            # pylint:disable=no-member
            sha = hashlib.sha3_256()
        elif hashtype == HashTypes.BLAKE2B:
            sha = hashlib.blake2b(digest_size=32)
        else:
            raise NotImplementedError
        sha.update(data)
        hash_ = sha.hexdigest()
        file_name = self.rng.next_file_name(8)
        path_to_file = os.path.join('tmp', file_name)
        while os.path.exists(path_to_file):
            file_name = self.rng.next_file_name(8)
            path_to_file = os.path.join('tmp', file_name)

        with open(path_to_file, 'wb') as file:
            file.write(data)

        if hashtype == HashTypes.SHA1:
            file_hash = file_sha1hex(path_to_file)
        elif hashtype == HashTypes.SHA2:
            file_hash = file_sha2hex(path_to_file)
        elif hashtype == HashTypes.SHA3:
            file_hash = file_sha3hex(path_to_file)
        elif hashtype == HashTypes.BLAKE2B:
            file_hash = file_blake2b_hex(path_to_file)
        else:
            raise NotImplementedError
        self.assertEqual(hash_, file_hash)

    def test_random_dir(self):
        """ Test building random directories with supported SHA hash types. """
        for hashtype in HashTypes:
            self.do_test_random_dir(hashtype)
Esempio n. 9
0
class TestDropFromU(unittest.TestCase):
    """ Test the drop_from_u_dir functionality. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def populate_tree(self, tree, data_path, u_dir, hashtype):
        """
        Generate nnn and nnn unique random values, where nnn is at least 16.
        """
        nnn = 16 + self.rng.next_int16(16)
        # DEBUG
        # print("nnn = %d" % nnn)
        # EnnnD

        values = []
        hashes = []
        for count in range(nnn):
            # generate datum ------------------------------
            datum = self.rng.some_bytes(32 + self.rng.next_int16(32))
            values.append(datum)

            # generate hash = bin_key ----------------------
            if hashtype == HashTypes.SHA1:
                sha = hashlib.sha1()
            elif hashtype == HashTypes.SHA2:
                sha = hashlib.sha256()
            elif hashtype == HashTypes.SHA3:
                sha = hashlib.sha3_256()
            elif hashtype == HashTypes.BLAKE2B:
                sha = hashlib.blake2b(digest_size=32)
            else:
                raise NotImplementedError
            sha.update(datum)
            bin_key = sha.digest()
            hex_key = sha.hexdigest()
            hashes.append(bin_key)

            # write data file -----------------------------
            file_name = 'value%04d' % count
            path_to_file = os.path.join(data_path, file_name)
            with open(path_to_file, 'wb') as file:
                # DEBUG
                # print("writing %s to %s" % (hex_key, path_to_file))
                # END
                file.write(datum)

            # insert leaf into tree -----------------------
            # path_from_top = os.path.join(top_name, file_name)
            leaf = NLHLeaf(file_name, bin_key, hashtype)
            tree.insert(leaf)

            # DEBUG
            # print("  inserting <%s %s>" % (leaf.name, leaf.hex_hash))
            # END

            # write data into uDir ------------------------
            u_dir.put_data(datum, hex_key)
        return values, hashes

    def generate_udt(self, struc, hashtype):
        """
        Generate under ./tmp a data directory with random content,
        a uDir containing the same data, and an NLHTree that matches.

        uDir has the directory structure (DIR_FLAT, DIR16x16, DIR256x256,
        etc requested.  Hashes are SHA1 if using SHA1 is True, SHA256
        otherwise.

        values is a list of binary values, each the content of a file
        under dataDir.  Each value contains a non-zero number of bytes.

        hashes is a list of the SHA hashes of the values.  Each hash
        is a binary value.  If using SHA1 it consists of 20 bytes.

        return uPath, data_path, tree, hashes, values
        """

        # make a unique U directory under ./tmp/
        os.makedirs('tmp', mode=0o755, exist_ok=True)
        u_root_name = self.rng.next_file_name(8)
        u_path = os.path.join('tmp', u_root_name)
        while os.path.exists(u_path):
            u_root_name = self.rng.next_file_name(8)
            u_path = os.path.join('tmp', u_root_name)

        # DEBUG
        # print("u_root_name = %s" % u_root_name)
        # END

        # create uDir and the NLHTree
        u_dir = UDir(u_path, struc, hashtype)
        self.assertTrue(os.path.exists(u_path))

        # make a unique data directory under tmp/
        data_tmp = self.rng.next_file_name(8)
        tmp_path = os.path.join('tmp', data_tmp)
        while os.path.exists(tmp_path):
            data_tmp = self.rng.next_file_name(8)
            tmp_path = os.path.join('tmp', data_tmp)

        # dataDir must have same base name as NLHTree
        top_name = self.rng.next_file_name(8)
        data_path = os.path.join(tmp_path, top_name)
        os.makedirs(data_path, mode=0o755)

        # DEBUG
        # print("data_tmp = %s" % data_tmp)
        # print("top_name = %s" % top_name)
        # print('data_path = %s' % data_path)
        # END

        tree = NLHTree(top_name, hashtype)
        values, hashes = self.populate_tree(tree, data_path, u_dir, hashtype)
        return u_path, data_path, tree, hashes, values

    # ---------------------------------------------------------------

    def do_test_with_ephemeral_tree(self, struc, hashtype):
        """
        Generate a tmp/ subdirectory containing a quasi-random data
        directory and corresponding uDir and NLHTree serialization.

        We use the directory strucure (struc) and hash type (hashtype)
        indicated, running various consistency tests on the three.
        """

        u_path, data_path, tree, hashes, values = self.generate_udt(
            struc, hashtype)

        # DEBUG
        # print("TREE:\n%s" % tree)
        # END
        # verify that the dataDir matches the nlhTree
        tree2 = NLHTree.create_from_file_system(data_path, hashtype)
        # DEBUG
        # print("TREE2:\n%s" % tree2)
        # END
        self.assertEqual(tree2, tree)

        nnn = len(values)             # number of values present
        hex_hashes = []
        for count in range(nnn):
            hex_hashes.append(hexlify(hashes[count]).decode('ascii'))

        ndxes = [ndx for ndx in range(nnn)]  # indexes into lists
        self.rng.shuffle(ndxes)         # shuffled

        kkk = self.rng.next_int16(nnn)   # we will drop this many indexes

        # DEBUG
        # print("dropping %d from %d elements" % (kkk, nnn))
        # END

        drop_me = ndxes[0:kkk]        # indexes of values to drop
        keep_me = ndxes[kkk:]         # of those which should still be present

        # construct an NLHTree containing values to be dropped from uDir
        clone = tree.clone()
        for count in keep_me:
            name = 'value%04d' % count
            clone.delete(name)     # the parameter is a glob !

        # these values should be absent from q: they won't be dropped from uDir
        for count in keep_me:
            name = 'value%04d' % count
            xxx = clone.find(name)
            self.assertEqual(len(xxx), 0)

        # these values shd still be present in clone: they'll be dropped from
        # UDir
        for count in drop_me:
            name = 'value%04d' % count
            xxx = clone.find(name)
            self.assertEqual(len(xxx), 1)

        # the clone subtree contains those elements which will be dropped
        # from uDir
        unmatched = clone.drop_from_u_dir(u_path)               # was unmatched

        # DEBUG
        # for x in unmatched:  # (relPath, hash)
        #    print("unmatched: %s %s" % (x[0], x[1]))
        # END
        self.assertEqual(len(unmatched), 0)

        u_dir = UDir(u_path, struc, hashtype)
        self.assertTrue(os.path.exists(u_path))

        # these values should still be present in uDir
        for count in keep_me:
            hex_hash = hex_hashes[count]
            self.assertTrue(u_dir.exists(hex_hash))

        # these values should NOT be present in UDir
        for count in drop_me:
            hex_hash = hex_hashes[count]
            self.assertFalse(u_dir.exists(hex_hash))

    def test_with_ephemeral_tree(self):
        """
        Generate tmp/ subdirectories containing a quasi-random data
        directory and corresponding uDir and NLHTree serialization,
        using various directory structures and hash types.
        """
        for struc in DirStruc:
            for hashtype in HashTypes:
                self.do_test_with_ephemeral_tree(struc, hashtype)
Esempio n. 10
0
class TestMerkleLeaf(unittest.TestCase):
    """ Test MerkleLeaf functionality. """
    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################

    # actual unit tests #############################################
    def do_test_simple_constructor(self, hashtype):
        """ Test constructor for specific SHA type. """

        check_hashtype(hashtype)
        if hashtype == HashTypes.SHA1:
            sha = XLSHA1()
        elif hashtype == HashTypes.SHA2:
            sha = XLSHA2()
        elif hashtype == HashTypes.SHA3:
            sha = XLSHA3()
        elif hashtype == HashTypes.BLAKE2B:
            sha = XLBLAKE2B_256()
        else:
            raise NotImplementedError

        file_name = self.rng.next_file_name(8)
        nnn = self.rng.some_bytes(8)
        sha.update(nnn)
        hash0 = sha.digest()

        leaf0 = MerkleLeaf(file_name, hashtype, hash0)
        self.assertEqual(file_name, leaf0.name)
        self.assertEqual(hash0, leaf0.bin_hash)

        file_name2 = file_name
        while file_name2 == file_name:
            file_name2 = self.rng.next_file_name(8)
        nnn = self.rng.some_bytes(8)
        self.rng.next_bytes(nnn)
        sha.update(nnn)
        hash1 = sha.digest()
        leaf1 = MerkleLeaf(file_name2, hashtype, hash1)
        self.assertEqual(file_name2, leaf1.name)
        self.assertEqual(hash1, leaf1.bin_hash)

        self.assertTrue(leaf0 == leaf0)
        self.assertFalse(leaf0 == leaf1)

        # XXX USE NLHTree instead
        # pair0    = leaf0.toPair()
        # leaf0bis = MerkleLeaf.createFromPair(pair0)
        # self.assertEqual(leaf0bis, leaf0)

        # pair1    = leaf1.toPair()
        # leaf1bis = MerkleLeaf.createFromPair(pair1)
        # self.assertEqual(leaf1bis, leaf1)

    def test_simple_constructor(self):
        """ Test constructor for various hash types. """
        for hashtype in HashTypes:
            self.do_test_simple_constructor(hashtype=hashtype)
Esempio n. 11
0
class TestFieldImpl(unittest.TestCase):

    def setUp(self):
        self.rng = SimpleRNG(time.time())

#       data = StringIO(ZOGGERY_PROTO_SPEC)
#       p = StringProtoSpecParser(data)   # data should be file-like
#       self.str_obj_model = p.parse()     # object model from string serialization
# self.proto_name = self.str_obj_model.name  # the dotted name of the
# protocol

    def tearDown(self):
        pass

    # utility functions #############################################

    def make_registries(self, protocol):
        node_reg = R.NodeReg()
        proto_reg = R.ProtoReg(protocol, node_reg)
        msg_reg = R.MsgReg(proto_reg)
        return (node_reg, proto_reg, msg_reg)

    def le_msg_values(self):
        """ returns a list """
        timestamp = int(time.time())
        node_id = [0] * 20
        key = [0] * 20
        length = self.rng.next_int32(256 * 256)
        # let's have some random bytes
        self.rng.next_bytes(node_id)
        self.rng.next_bytes(key)
        by_ = 'who is responsible'
        path = '/home/jdd/tarballs/something.tar.gz'
        return [timestamp, node_id, key, length, by_, path]

    def lil_big_msg_values(self):
        """
        This returns a list of random-ish values in order by field type
        so that values[_F_FLOAT], for example, is a random float value.
        """
        values = []

        # 2016-03-30 This is NOT in sync with littleBigTest.py,
        #   because I have added a None for lMsg at _L_MSG

        values.append(self.rng.next_boolean())       # vBoolReqField         0
        values.append(self.rng.next_int16())         # vEnumReqField         1

        values.append(self.rng.next_int32())         # vInt32ReqField        2
        values.append(self.rng.next_int64())         # vInt64ReqField        3

        values.append(self.rng.next_int32())         # vuInt32ReqField       4
        values.append(self.rng.next_int32())         # vuInt64ReqField       5
        values.append(self.rng.next_int64())         # vsInt32ReqField       6
        values.append(self.rng.next_int64())         # vsInt64ReqField       7

        values.append(self.rng.next_int32())         # fsInt32ReqField       8
        values.append(self.rng.next_int32())         # fuInt32ReqField       9
        values.append(self.rng.next_real())          # fFloatReqField        10

        values.append(self.rng.next_int64())         # fsInt64ReqField       11
        values.append(self.rng.next_int64())         # fuInt64ReqField       12

        values.append(self.rng.next_real())          # fDoubleReqField       13

        # lStringReqField       14
        values.append(self.rng.next_file_name(16))

        rnd_len = 16 + self.rng.next_int16(49)
        byte_buf = bytearray(rnd_len)
        self.rng.next_bytes(byte_buf)
        values.append(bytes(byte_buf))               # lBytesReqField        15

        values.append(None)                         # <-------- for lMsg    16

        b128_buf = bytearray(16)
        self.rng.next_bytes(b128_buf)
        values.append(bytes(b128_buf))               # fBytes16ReqField      17

        b160_buf = bytearray(20)
        self.rng.next_bytes(b160_buf)
        values.append(bytes(b160_buf))               # fBytes20ReqField      18

        b256_buf = bytearray(32)
        self.rng.next_bytes(b256_buf)
        values.append(bytes(b256_buf))               # fBytes32ReqField      19

        return values

    # actual unit tests #############################################

    def check_field_impl_against_spec(self,
                                      proto_name, msg_name,   # not actually tested
                                      field_spec, value):    # significant for tests

        self.assertIsNotNone(field_spec)
        dotted_name = "%s.%s" % (proto_name, msg_name)
        cls = make_field_class(dotted_name, field_spec)         # a class
        if '__dict__' in dir(cls):
            print('\nGENERATED FieldImpl CLASS DICTIONARY')
            for exc in list(cls.__dict__.keys()):
                print("  %-20s %s" % (exc, cls.__dict__[exc]))

        self.assertIsNotNone(cls)
        file = cls(value)                                      # an instance
        self.assertIsNotNone(file)
        self.assertTrue(isinstance(file, cls))

        # instance attributes -----------------------------
        # we verify that the properties work correctly

        self.assertEqual(field_spec.name, file._name)
        self.assertEqual(field_spec.field_type_ndx, file.field_type)
        self.assertEqual(field_spec.quantifier, file.quantifier)
        self.assertEqual(field_spec.field_nbr, file.field_nbr)
        self.assertIsNone(file.default)          # not an elegant test

        # instance attribute ------------------------------
        # we can read back the value assigned to the instance

        self.assertEqual(value, file.value)

        # with slots enabled, this is never seen ----------
        # because __dict__ is not in the list of valid
        # attributes for f
        if '__dict__' in dir(file):
            print('\nGENERATED FieldImpl INSTANCE DICTIONARY')
            for item in list(file.__dict__.keys()):
                print("%-20s %s" % (item, file.__dict__[item]))

    def test_field_impl(self):

        node_reg, proto_reg, msg_reg = self.make_registries(
            PROTOCOL_UNDER_TEST)
        values = self.lil_big_msg_values()

        # DEBUG
        print("testFieldImpl: there are %d values" % len(values))
        # END

        # There are 18 values corresponding to the 18 field types;
        # _L_MSG should be skipped

        for tstamp in range(FieldTypes.F_BYTES32 + 1):
            # DEBUG
            print("testFieldImpl: t = %d" % tstamp)
            # END
            if tstamp == FieldTypes.L_MSG:
                continue

            # default quantifier is Q_REQ_, default is None

            field_name = 'field%d' % tstamp
            field_spec = M.FieldSpec(
                msg_reg, field_name, tstamp, field_nbr=tstamp + 100)

            self.check_field_impl_against_spec(
                PROTOCOL_UNDER_TEST, MSG_UNDER_TEST,
                field_spec, values[tstamp])

    # TEST FIELD SPEC -----------------------------------------------

    def do_field_spec_test(self, name, field_type, quantifier=M.Q_REQUIRED,
                           field_nbr=0, default=None):

        node_reg, proto_reg, msg_reg = self.make_registries(
            PROTOCOL_UNDER_TEST)

        # XXX Defaults are ignored for now.
        file = M.FieldSpec(
            msg_reg,
            name,
            field_type,
            quantifier,
            field_nbr,
            default)

        self.assertEqual(name, file.name)
        self.assertEqual(field_type, file.field_type_ndx)
        self.assertEqual(quantifier, file.quantifier)
        self.assertEqual(field_nbr, file.field_nbr)
        if default is not None:
            self.assertEqual(default, file.default)

        expected_repr = "%s %s%s @%d \n" % (
            name, file.field_type_name, M.q_name(quantifier), field_nbr)
        # DEFAULTS NOT SUPPORTED
        self.assertEqual(expected_repr, file.__repr__())

    def test_quantifiers(self):
        q_name = M.q_name
        self.assertEqual('', q_name(M.Q_REQUIRED))
        self.assertEqual('?', q_name(M.Q_OPTIONAL))
        self.assertEqual('*', q_name(M.Q_STAR))
        self.assertEqual('+', q_name(M.Q_PLUS))

    def test_field_spec(self):
        # default is not implemented yet
        self.do_field_spec_test('foo', FieldTypes.V_UINT32, M.Q_REQUIRED, 9)
        self.do_field_spec_test('bar', FieldTypes.V_SINT32, M.Q_STAR, 17)
        self.do_field_spec_test(
            'node_id',
            FieldTypes.F_BYTES20,
            M.Q_OPTIONAL,
            92)
        self.do_field_spec_test('tix', FieldTypes.V_BOOL, M.Q_PLUS, 147)
Esempio n. 12
0
class TestLittleBig(unittest.TestCase):

    def setUp(self):
        self.rng = SimpleRNG(time.time())
        data = StringIO(LITTLE_BIG_PROTO_SPEC)
        ppp = StringProtoSpecParser(data)   # data should be file-like
        self.str_obj_model = ppp.parse()     # object model from string serialization
        self.proto_name = self.str_obj_model.name  # the dotted name of the protocol

    def tearDown(self):
        pass

    # utility functions #############################################

    def lil_big_msg_values(self):
        values = []
        # XXX these MUST be kept in sync with littleBigTest.py
        values.append(self.rng.next_boolean())       # vBoolReqField
        values.append(self.rng.next_int16())         # vEnumReqField

        values.append(self.rng.next_int32())         # vuInt32ReqField
        values.append(self.rng.next_int32())         # vuInt64ReqField
        values.append(self.rng.next_int64())         # vsInt32ReqField
        values.append(self.rng.next_int64())         # vsInt64ReqField

        # #vuInt32ReqField
        # #vuInt64ReqField

        values.append(self.rng.next_int32())         # fsInt32ReqField
        values.append(self.rng.next_int32())         # fuInt32ReqField
        values.append(self.rng.next_real())          # fFloatReqField

        values.append(self.rng.next_int64())         # fsInt64ReqField
        values.append(self.rng.next_int64())         # fuInt64ReqField
        values.append(self.rng.next_real())          # fDoubleReqField

        values.append(self.rng.next_file_name(16))    # lStringReqField

        rnd_len = 16 + self.rng.next_int16(49)
        byte_buf = bytearray(rnd_len)
        self.rng.next_bytes(byte_buf)
        values.append(bytes(byte_buf))               # lBytesReqField

        b128_buf = bytearray(16)
        self.rng.next_bytes(b128_buf)
        values.append(bytes(b128_buf))               # fBytes16ReqField

        b160_buf = bytearray(20)
        self.rng.next_bytes(b160_buf)
        values.append(bytes(b160_buf))               # fBytes20ReqField

        b256_buf = bytearray(32)
        self.rng.next_bytes(b256_buf)
        values.append(bytes(b256_buf))               # fBytes32ReqField

        return values

    # actual unit tests #############################################
    def check_field_impl_against_spec(
            self, proto_name, msg_name, field_spec, value):
        self.assertIsNotNone(field_spec)
        dotted_name = "%s.%s" % (proto_name, msg_name)
        cls = make_field_class(dotted_name, field_spec)
        if '__dict__' in dir(cls):
            print('\nGENERATED FieldImpl CLASS DICTIONARY')
            for exc in list(cls.__dict__.keys()):
                print("%-20s %s" % (exc, cls.__dict__[exc]))

        self.assertIsNotNone(cls)
        file = cls(value)
        self.assertIsNotNone(file)

        # class attributes --------------------------------
        self.assertEqual(field_spec.name, file.name)
        self.assertEqual(field_spec.field_type_ndx, file.field_type)
        self.assertEqual(field_spec.quantifier, file.quantifier)
        self.assertEqual(field_spec.field_nbr, file.field_nbr)
        self.assertIsNone(file.default)          # not an elegant test

        # instance attribute ------------------------------
        self.assertEqual(value, file.value)

        # with slots enabled, this is never seen ----------
        # because __dict__ is not in the list of valid
        # attributes for f
        if '__dict__' in dir(file):
            print('\nGENERATED FieldImpl INSTANCE DICTIONARY')
            for item in list(file.__dict__.keys()):
                print("%-20s %s" % (item, file.__dict__[item]))     # GEEP

    def test_field_impl(self):
        msg_spec = self.str_obj_model.msgs[0]

        # the fields in this imaginary logEntry
        values = self.lil_big_msg_values()

        for i in range(len(msg_spec)):
            print(
                "\nDEBUG: field %u ------------------------------------------------------" %
                i)
            field_spec = msg_spec[i]
            self.check_field_impl_against_spec(
                self.proto_name, msg_spec.name, field_spec, values[i])

    def test_caching(self):
        self.assertTrue(isinstance(self.str_obj_model, M.ProtoSpec))
        # XXX A HACK WHILE WE CHANGE INTERFACE ------------
        msg_spec = self.str_obj_model.msgs[0]
        name = msg_spec.name

        cls0 = make_msg_class(self.str_obj_model, name)
        # DEBUG
        print("Constructed Clz0 name is '%s'" % cls0.name)
        # END
        self.assertEqual(name, cls0.name)
        cls1 = make_msg_class(self.str_obj_model, name)
        self.assertEqual(name, cls1.name)

        # END HACK ----------------------------------------
        # we cache classe, so the two should be the same
        self.assertEqual(id(cls0), id(cls1))

        # chan    = Channel(BUFSIZE)
        values = self.lil_big_msg_values()
        lil_big_msg0 = cls0(values)
        lil_big_msg1 = cls0(values)
        # we don't cache instances, so these will differ
        self.assertNotEqual(id(lil_big_msg0), id(lil_big_msg1))

        field_spec = msg_spec[0]
        dotted_name = "%s.%s" % (self.proto_name, msg_spec.name)
        f0cls = make_field_class(dotted_name, field_spec)
        f1cls = make_field_class(dotted_name, field_spec)
        self.assertEqual(id(f0cls), id(f1cls))

    def test_little_big(self):
        self.assertIsNotNone(self.str_obj_model)
        self.assertTrue(isinstance(self.str_obj_model, M.ProtoSpec))
        self.assertEqual('org.xlattice.fieldz.test.littleBigProto',
                         self.str_obj_model.name)

        self.assertEqual(0, len(self.str_obj_model.enums))
        self.assertEqual(1, len(self.str_obj_model.msgs))
        self.assertEqual(0, len(self.str_obj_model.seqs))

        msg_spec = self.str_obj_model.msgs[0]

        # Create a channel ------------------------------------------
        # its buffer will be used for both serializing the instance
        # data and, by deserializing it, for creating a second instance.
        chan = Channel(BUFSIZE)
        buf = chan.buffer
        self.assertEqual(BUFSIZE, len(buf))

        # create the LittleBigMsg class ------------------------------
        little_big_msg_cls = make_msg_class(self.str_obj_model, msg_spec.name)

        # -------------------------------------------------------------
        # XXX the following fails because field 2 is seen as a property
        # instead of a list
        if False:        # DEBUGGING
            print('\nLittleBigMsg CLASS DICTIONARY')
            for (ndx, key) in enumerate(little_big_msg_cls.__dict__.keys()):
                print(
                    "%3u: %-20s %s" %
                    (ndx, key, little_big_msg_cls.__dict__[key]))
        # -------------------------------------------------------------

        # create a message instance ---------------------------------
        values = self.lil_big_msg_values()            # quasi-random values
        lil_big_msg = little_big_msg_cls(values)

        # __setattr__ in MetaMsg raises exception on any attempt
        # to add new attributes.  This works at the class level but
        # NOT at the instance level
        #
        if True:
            try:
                lil_big_msg.foo = 42
                self.fail(
                    "ERROR: attempt to assign new instance attribute succeeded")
            except AttributeError as a_exc:
                # DEBUG
                print(
                    "ATTR ERROR ATTEMPTING TO SET lilBigMsg.foo: " +
                    str(a_exc))
                # END
                pass

        if '__dict__' in dir(lil_big_msg):
            print('\nlilBigMsg INSTANCE DICTIONARY')
            for exc in list(lil_big_msg.__dict__.keys()):
                print("%-20s %s" % (exc, lil_big_msg.__dict__[exc]))

        # lilBigMsg.name is a property
        try:
            lil_big_msg.name = 'boo'
            self.fail("ERROR: attempt to change message name succeeded")
        except AttributeError:
            pass

        self.assertEqual(msg_spec.name, lil_big_msg.name)
        # we don't have any nested enums or messages
        self.assertEqual(0, len(lil_big_msg.enums))
        self.assertEqual(0, len(lil_big_msg.msgs))

        self.assertEqual(17, len(lil_big_msg.field_classes))
        # number of fields in instance
        self.assertEqual(17, len(lil_big_msg))
        for i in range(len(lil_big_msg)):
            self.assertEqual(values[i], lil_big_msg[i].value)

        # serialize the object to the channel -----------------------
        print("\nDEBUG: PHASE A ######################################")
        nnn = lil_big_msg.write_stand_alone(chan)

        old_position = chan.position
        chan.flip()
        self.assertEqual(old_position, chan.limit)
        self.assertEqual(0, chan.position)

        # deserialize the channel, making a clone of the message ----
        (read_back, nn2) = little_big_msg_cls.read(
            chan, self.str_obj_model)  # sOM is protoSpec
        self.assertIsNotNone(read_back)
        self.assertEqual(nnn, nn2)

        # verify that the messages are identical --------------------
        self.assertTrue(lil_big_msg.__eq__(read_back))

        print("\nDEBUG: PHASE B ######################################")
        # produce another message from the same values --------------
        lil_big_msg2 = little_big_msg_cls(values)
        chan2 = Channel(BUFSIZE)
        nnn = lil_big_msg2.write_stand_alone(chan2)
        chan2.flip()
        (copy2, nn3) = little_big_msg_cls.read(chan2, self.str_obj_model)
        self.assertIsNotNone(copy2)
        self.assertEqual(nnn, nn3)
        self.assertTrue(lil_big_msg.__eq__(copy2))
        self.assertTrue(lil_big_msg2.__eq__(copy2))

        # test clear()
        chan2.position = 97
        chan2.limit = 107
        chan2.clear()
        self.assertEqual(0, chan2.limit)
        self.assertEqual(0, chan2.position)
Esempio n. 13
0
class TestNLHTree2(unittest.TestCase):
    """ Test trees derived from various quasi-random directory structures. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions ---------------------------------------------

    def get_two_unique_directory_names(self):
        """ Make two unique directory names. """

        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        self.assertTrue(len(dir_name1) > 0)
        self.assertTrue(len(dir_name2) > 0)
        self.assertTrue(dir_name1 != dir_name2)
        return (dir_name1, dir_name2)

    def make_one_named_test_directory(self, name, depth, width):
        """
        Create a test directory below tmp/ with specified characteristics.
        """

        dir_path = "tmp/%s" % name
        if os.path.exists(dir_path):
            shutil.rmtree(dir_path)
        self.rng.next_data_dir(dir_path, depth, width, 32)
        return dir_path

    def make_two_test_directories(self, depth, width):
        """ Make two distinct quasi-random test directories below tmp/. """

        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path1 = self.make_one_named_test_directory(dir_name1, depth, width)

        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path2 = self.make_one_named_test_directory(dir_name2, depth, width)

        return (dir_name1, dir_path1, dir_name2, dir_path2)

    # unit tests ----------------------------------------------------

    def test_pathless_unbound(self):
        """ Test the constructor using various hash types. """

        for hashtype in [HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3, ]:
            self.do_test_pathless_unbound(hashtype)

    def do_test_pathless_unbound(self, hashtype):
        """
        Test constructor using two directories and a specific hash type.
        """

        (dir_name1, dir_name2) = self.get_two_unique_directory_names()

        check_hashtype(hashtype)
        tree1 = NLHTree(dir_name1, hashtype)
        self.assertEqual(dir_name1, tree1.name)
        self.assertEqual(tree1.hashtype, hashtype)

        tree2 = NLHTree(dir_name2, hashtype)
        self.assertEqual(dir_name2, tree2.name)
        self.assertEqual(tree2.hashtype, hashtype)

        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)
        self.assertFalse(tree1 is None)

        tree1c = tree1.clone()
        self.assertEqual(tree1c, tree1)

    def test_bound_flat_dirs(self):
        """
        Test directory is single level, with four data files, using
        various hash types.
        """
        for hashtype in HashTypes:
            self.do_test_bound_flat_dirs(hashtype)

    def do_test_bound_flat_dirs(self, hashtype):
        """
        Test directory is single level, with four data files, using
        specific hash type.
        """

        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(ONE, FOUR)
        tree1 = NLHTree.create_from_file_system(dir_path1, hashtype)
        self.assertEqual(dir_name1, tree1.name, True)
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(FOUR, len(nodes1))

        tree2 = NLHTree.create_from_file_system(dir_path2, hashtype)
        self.assertEqual(dir_name2, tree2.name)
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(FOUR, len(nodes2))

        self.assertEqual(tree1, tree1)
        self.assertFalse(tree1 == tree2)
        self.assertFalse(tree1 is None)

        tree1c = tree1.clone()
        self.assertEqual(tree1c, tree1)

    def test_bound_needle_dirs1(self):
        """
        Test directories four deep with one data file at the lowest level
        using various hash types.
        """
        for hashtype in HashTypes:
            self.do_test_bound_needle_dirs(hashtype)

    def do_test_bound_needle_dirs(self, hashtype):
        """
        Test directories four deep with one data file at the lowest level
        using specific hash type.
        """
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(FOUR, ONE)
        tree1 = NLHTree.create_from_file_system(dir_path1, hashtype)

        self.assertEqual(dir_name1, tree1.name)
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(ONE, len(nodes1))

        tree2 = NLHTree.create_from_file_system(dir_path2, hashtype)
        self.assertEqual(dir_name2, tree2.name)
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(ONE, len(nodes2))

        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)

        tree1c = tree1.clone()
        self.assertEqual(tree1c, tree1)
Esempio n. 14
0
class TestNLHLeaf(unittest.TestCase):
    """ Test NLHLeaf-related functions. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################

    # actual unit tests #############################################
    def do_test_simple_constructor(self, hashtype):
        """ Test constructor for specific hash. """

        check_hashtype(hashtype)
        if hashtype == HashTypes.SHA1:
            sha = hashlib.sha1()
        elif hashtype == HashTypes.SHA2:
            sha = hashlib.sha256()
        elif hashtype == HashTypes.SHA3:
            sha = hashlib.sha3_256()
        elif hashtype == HashTypes.BLAKE2B:
            sha = hashlib.blake2b(digest_size=32)
        else:
            raise NotImplementedError

        name = self.rng.next_file_name(8)
        nnn = self.rng.some_bytes(8)
        self.rng.next_bytes(nnn)
        sha.update(nnn)
        hash0 = sha.digest()

        leaf0 = NLHLeaf(name, hash0, hashtype)
        self.assertEqual(name, leaf0.name)
        self.assertEqual(hash0, leaf0.bin_hash)

        name2 = name
        while name2 == name:
            name2 = self.rng.next_file_name(8)
        nnn = self.rng.some_bytes(8)
        self.rng.next_bytes(nnn)
        sha.update(nnn)
        hash1 = sha.digest()
        leaf1 = NLHLeaf(name2, hash1, hashtype)
        self.assertEqual(name2, leaf1.name)
        self.assertEqual(hash1, leaf1.bin_hash)

        self.assertEqual(leaf0, leaf0)
        self.assertEqual(leaf1, leaf1)
        self.assertFalse(leaf0 == leaf1)

        leaf0c = leaf0.clone()
        self.assertEqual(leaf0c, leaf0)

        leaf1c = leaf1.clone()
        self.assertEqual(leaf1c, leaf1)

    def test_simplest_constructor(self):
        """ Test simple constructor for various hashes. """

        for hashtype in HashTypes:
            self.do_test_simple_constructor(hashtype)
Esempio n. 15
0
class TestFieldTypes(unittest.TestCase):
    """
    Actually tests the method used for instantiating and importing
    an instance of the FieldTypes class.
    """
    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def test_new_fieldtypes(self):
        """
        Test the new definition of FieldTypes introduced 2017-01-30.
        """
        self.assertEqual(len(FieldTypes), FieldTypes.F_BYTES32.value + 1)
        for ndx, _ in enumerate(FieldTypes):
            self.assertEqual(_.value, ndx)
            # round trip member to sym and back to member
            self.assertEqual(FieldTypes.from_sym(_.sym), _)

    def test_constants(self):
        """
        Verify that our constants are immutable and conversion between
        string and integer forms works as expected.
        """
        self.assertEqual(len(FieldTypes), 18)

        # pylint: disable=unsubscriptable-object
        self.assertEqual(FieldTypes.V_BOOL.value, 0)
        self.assertEqual(FieldTypes.V_BOOL.sym, 'vbool')

        self.assertEqual(FieldTypes.F_BYTES32.value, len(FieldTypes) - 1)
        self.assertEqual(FieldTypes.F_BYTES32.sym, 'fbytes32')

    def test_len_funcs(self):
        """
        Verify that varint length functions return correct values.

        Tests are performed using randomly selected field numbers
        (in the range 0 .. (2^16)-1) and integer values in the same
        range.
        """
        ndx = self.rng.next_int16()  # random field number
        value = self.rng.next_int16()  # random integer value

        # == varint types ===========================================
        # ERROR because field_hdr_len 2nd param should be PrimType
        # ********************************************************
        len_ = raw.field_hdr_len(ndx, FieldTypes.V_BOOL)
        self.assertEqual(len_ + 1, typed.vbool_len(True, ndx))
        self.assertEqual(len_ + 1, typed.vbool_len(False, ndx))

        len_ = raw.field_hdr_len(ndx, FieldTypes.V_ENUM)
        zzz = len_ + raw.length_as_varint(value)
        self.assertEqual(zzz, typed.venum_len(value, ndx))
        # self.assertEqual( x, typed.vEnumLen(-x, n) )

        value = self.rng.next_int32()
        self.assertTrue(value >= 0)

        len_ = raw.field_hdr_len(ndx, FieldTypes.V_UINT32)
        zzz = len_ + raw.length_as_varint(value)
        self.assertEqual(zzz, typed.vuint32_len(value, ndx))

        value = self.rng.next_int32()
        self.assertTrue(value >= 0)
        value = value - 0x80000000

        len_ = raw.field_hdr_len(ndx, FieldTypes.V_SINT32)
        ppp = typed.encode_sint32(value)
        zzz = len_ + raw.length_as_varint(ppp)
        self.assertEqual(zzz, typed.vsint32_len(value, ndx))

        value = self.rng.next_int64()
        self.assertTrue(value >= 0)

        len_ = raw.field_hdr_len(ndx, FieldTypes.V_UINT64)
        zzz = len_ + raw.length_as_varint(value)
        self.assertEqual(zzz, typed.vuint64_len(value, ndx))

        value = self.rng.next_int64()
        self.assertTrue(value >= 0)
        value = value - 0x8000000000000000

        len_ = raw.field_hdr_len(ndx, FieldTypes.V_SINT64)
        ppp = typed.encode_sint64(value)
        zzz = len_ + raw.length_as_varint(ppp)
        self.assertEqual(zzz, typed.vsint64_len(value, ndx))

        # == fixed length 4 byte ====================================
        value = self.rng.next_int64()  # value should be ignored

        self.assertTrue(value >= 0)
        value = value - 0x8000000000000000

        # x is a signed 64 bit value whose value should be irrelevant
        len_ = raw.field_hdr_len(ndx, FieldTypes.F_UINT32)
        self.assertEqual(len_ + 4, typed.fuint32_len(value, ndx))

        len_ = raw.field_hdr_len(ndx, FieldTypes.F_SINT32)
        self.assertEqual(len_ + 4, typed.fsint32_len(value, ndx))

        len_ = raw.field_hdr_len(ndx, FieldTypes.F_FLOAT)
        self.assertEqual(len_ + 4, typed.ffloat_len(value, ndx))

        # == fixed length 8 byte ====================================
        # n is that signed 64 bit value whose value should be irrelevant
        len_ = raw.field_hdr_len(ndx, FieldTypes.F_UINT64)
        self.assertEqual(len_ + 8, typed.fuint64_len(value, ndx))
        len_ = raw.field_hdr_len(ndx, FieldTypes.F_SINT64)
        self.assertEqual(len_ + 8, typed.fsint64_len(value, ndx))
        len_ = raw.field_hdr_len(ndx, FieldTypes.F_DOUBLE)
        self.assertEqual(len_ + 8, typed.fdouble_len(value, ndx))

        # == LEN PLUS types =========================================
        def do_len_plus_test(length, ndx):
            """
            Verify that fields of interesting lengths have expected
            raw encodings.
            """
            string = [0] * length
            k = len(string)
            len_ = raw.field_hdr_len(ndx, FieldTypes.L_BYTES)
            expected_len = len_ + raw.length_as_varint(k) + k
            self.assertEqual(expected_len, typed.lbytes_len(string, ndx))

        # -- lString ---------------------------------------
        string = self.rng.next_file_name(256)
        len_ = raw.field_hdr_len(ndx, FieldTypes.L_STRING)
        k = len(string)
        expected_len = len_ + raw.length_as_varint(k) + k
        self.assertEqual(expected_len, typed.l_string_len(string, ndx))

        # -- lBytes ----------------------------------------
        do_len_plus_test(0x7f, ndx)
        do_len_plus_test(0x80, ndx)
        do_len_plus_test(0x3fff, ndx)
        do_len_plus_test(0x4000, ndx)

        # -- lMsg ------------------------------------------
        # XXX STUB

        # -- fixed length byte arrays -------------------------------
        buf = [0] * 512  # length functions should ignore actual size

        len_ = raw.field_hdr_len(ndx, FieldTypes.F_BYTES16)
        self.assertEqual(len_ + 16, typed.fbytes16_len(buf, ndx))

        len_ = raw.field_hdr_len(ndx, FieldTypes.F_BYTES20)
        self.assertEqual(len_ + 20, typed.fbytes20_len(buf, ndx))

        len_ = raw.field_hdr_len(ndx, FieldTypes.F_BYTES32)
        self.assertEqual(len_ + 32, typed.fbytes32_len(buf, ndx))
Esempio n. 16
0
class TestMerkleDoc(unittest.TestCase):
    """ Test MerkleTree functionality at the document level. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def get_two_unique_directory_names(self):
        """
        Get two candidate directory names, making sure that they differ.
        """
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        self.assertTrue(len(dir_name1) > 0)
        self.assertTrue(len(dir_name2) > 0)
        self.assertTrue(dir_name1 != dir_name2)
        return (dir_name1, dir_name2)

    def make_one_named_test_directory(self, name, depth, width):
        """
        Create a test directory with the name, depth, and width specified.
        The directory is under tmp/ ; subdirectories have random names
        and contents.
        """
        dir_path = "tmp/%s" % name
        if os.path.exists(dir_path):
            shutil.rmtree(dir_path)
        self.rng.next_data_dir(dir_path, depth, width, 32)
        return dir_path

    def make_two_test_directories(self, depth, width):
        """
        Create two test directories under tmp/ with distinct names but the
        depth and width specified.
        """
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path1 = self.make_one_named_test_directory(dir_name1, depth, width)

        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path2 = self.make_one_named_test_directory(dir_name2, depth, width)

        return (dir_name1, dir_path1, dir_name2, dir_path2)

    def verify_leaf_sha256(self, node, path_to_file):
        """
        Verify that the content keys of the named file match the SHA
        hash of its contents.
        """
        self.assertTrue(os.path.exists(path_to_file))
        with open(path_to_file, "rb") as file:
            data = file.read()
        self.assertFalse(data is None)
        sha = hashlib.sha256()
        sha.update(data)
        hash_ = sha.digest()
        self.assertEqual(hash_, node.bin_hash)

    def verify_tree_sha256(self, node, path_to_tree):
        """
        Verify that the names (content keys) of files below the node
        (a Merkletree) have correct content keys, matching the SHA
        hash of the files.
        """
        if node.nodes is None:
            self.assertEqual(None, node.bin_hash)
        else:
            hash_count = 0
            sha = hashlib.sha256()
            for node_ in node.nodes:
                path_to_node = os.path.join(path_to_tree, node_.name)
                if isinstance(node_, MerkleLeaf):
                    self.verify_leaf_sha256(node_, path_to_node)
                elif isinstance(node_, MerkleTree):
                    self.verify_tree_sha256(node_, path_to_node)
                else:
                    print("DEBUG: unknown node type!")
                    self.fail("unknown node type!")
                if node_.bin_hash is not None:
                    hash_count += 1
                    sha.update(node_.bin_hash)

            if hash_count == 0:
                self.assertEqual(None, node.bin_hash)
            else:
                self.assertEqual(sha.digest(), node.bin_hash)

    # actual unit tests #############################################

    def test_bound_flat_dirs(self):
        """test directory is single level, with four data files"""

        dir_name1, dir_path1, dir_name2, dir_path2 = \
            self.make_two_test_directories(ONE, FOUR)
        doc1 = MerkleDoc.create_from_file_system(dir_path1)
        # pylint: disable=no-member
        tree1 = doc1.tree
        # XXX This succeeds BUT pylint doesn't get this right: it sees
        # doc1.tree as a function
        self.assertTrue(isinstance(tree1, MerkleTree))

        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(FOUR, len(nodes1))
        self.verify_tree_sha256(tree1, dir_path1)

        doc2 = MerkleDoc.create_from_file_system(dir_path2)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(FOUR, len(nodes2))
        self.verify_tree_sha256(tree2, dir_path2)

        # pylint: disable=no-member
        self.assertTrue(tree1.equal(tree1))
        # pylint: disable=no-member
        self.assertFalse(tree1.equal(tree2))
        # pylint: disable=no-member
        self.assertFalse(tree1.equal(None))

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str)
        self.assertTrue(doc1.equal(doc1_rebuilt))  # MANGO

    def test_bound_needle_dirs(self):
        """test directories four deep with one data file at the lowest level"""
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(FOUR, ONE)
        doc1 = MerkleDoc.create_from_file_system(dir_path1)
        tree1 = doc1.tree
        # XXX This succeeds BUT pylint doesn't get this right: it sees
        # doc1.tree as a function
        self.assertTrue(isinstance(tree1, MerkleTree))

        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(ONE, len(nodes1))
        self.verify_tree_sha256(tree1, dir_path1)

        doc2 = MerkleDoc.create_from_file_system(dir_path2)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(ONE, len(nodes2))
        self.verify_tree_sha256(tree2, dir_path2)

        self.assertTrue(doc1.equal(doc1))
        self.assertFalse(doc1.equal(doc2))

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str)
#       # DEBUG
#       print "needle doc:\n" + doc1Str
#       print "rebuilt needle doc:\n" + doc1Rebuilt.toString()
#       # END
        self.assertTrue(doc1.equal(doc1_rebuilt))       # FOO
Esempio n. 17
0
class TestOptionz(unittest.TestCase):
    """ Test the basic Optionz classes. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################

    # actual unit tests #############################################

    def test_bare_optionz(self):
        """ Create an Optionz instance, check for expected attibutes. """

        my_optz = Z('fred')
        self.assertEqual(my_optz.name, 'fred')
        self.assertEqual(my_optz.desc, None)
        self.assertEqual(my_optz.epilog, None)
        self.assertEqual(len(my_optz), 0)

        my_optz = Z('frank', 'frivolous', 'fabulous')
        self.assertEqual(my_optz.name, 'frank')
        self.assertEqual(my_optz.desc, 'frivolous')
        self.assertEqual(my_optz.epilog, 'fabulous')
        self.assertEqual(len(my_optz), 0)

    def test_z_option(self):
        """ Populate an Optionz object, check for expected attr. """

        z_name = self.rng.next_file_name(8)
        z_desc = self.rng.next_file_name(64)
        z_epilog = self.rng.next_file_name(64)

        my_optz = Z(z_name, z_desc, z_epilog)

        self.assertEqual(my_optz.name, z_name)
        self.assertEqual(my_optz.desc, z_desc)
        self.assertEqual(my_optz.epilog, z_epilog)
        self.assertEqual(len(my_optz), 0)

        # booleans --------------------------------------------------

        b_dflt_val = True
        b_desc = "I'm small"
        bool_opt = BoolOption('bO', default=b_dflt_val, desc=b_desc)
        self.assertEqual(bool_opt.name, 'bO')
        self.assertEqual(bool_opt.default, b_dflt_val)
        self.assertEqual(bool_opt.desc, b_desc)

        #                        name    valType     default    desc
        b_check = my_optz.add_option('bO', ValType.BOOL, b_dflt_val, b_desc)
        self.assertEqual(len(my_optz), 1)
        self.assertEqual(bool_opt, b_check)

        # choice lists ----------------------------------------------

        # NOTE We should probably require that list elements be of
        # compatible types.  For the moment we just assume that elements
        # are all strings.

        # succeeds if default in list of choices ----------
        my_size = 2 + self.rng.next_int16(4)     # so in [2..5]
        choice = self.rng.next_file_name(8)
        choices = [choice]

        while len(choices) < my_size:
            if choice not in choices:
                choices.append(choice)
            choice = self.rng.next_file_name(8)

        c_dflt_val = choices[self.rng.next_int16(my_size)]
        c_desc = 'a list'
        choice_opt = ChoiceOption('cO', choices, c_dflt_val, c_desc)
        self.assertEqual(choice_opt.name, 'cO')
        self.assertEqual(choice_opt.choices, choices)
        self.assertEqual(choice_opt.default, c_dflt_val)
        self.assertEqual(choice_opt.desc, "a list")

        # fails if default is NOT in list of choices ------
        my_size = 2 + self.rng.next_int16(4)     # so in [2..5]
        choice = self.rng.next_file_name(8)
        b_choices = [choice]

        while len(b_choices) < my_size:
            if choice not in b_choices:
                b_choices.append(choice)
            choice = self.rng.next_file_name(8)

        dflt_val = self.rng.next_file_name(8)
        while dflt_val in choices:
            dflt_val = self.rng.next_file_name(8)

        try:
            ChoiceOption('bC', choices, default=dflt_val, desc="a list")
            self.fail('added default value not in list of choices')
        except BaseException:
            pass

        c_check = my_optz.add_choice_option('cO', choices, c_dflt_val, c_desc)
        self.assertEqual(len(my_optz), 2)
        self.assertEqual(choice_opt, c_check)

        # floats ----------------------------------------------------

        f_dflt_val = self.rng.next_real()
        f_desc = 'bubbly'
        float_opt = FloatOption('fO', default=f_dflt_val, desc=f_desc)
        self.assertEqual(float_opt.name, 'fO')
        self.assertEqual(float_opt.default, f_dflt_val)
        self.assertEqual(float_opt.desc, f_desc)

        #                        name    valType     default    desc
        f_check = my_optz.add_option('fO', ValType.FLOAT, f_dflt_val, f_desc)
        self.assertEqual(len(my_optz), 3)
        self.assertEqual(float_opt, f_check)

        # ints ------------------------------------------------------

        i_dflt_val = self.rng.next_int32()
        i_desc = 'discrete'
        int_opt = IntOption('iO', default=i_dflt_val, desc=i_desc)
        self.assertEqual(int_opt.name, 'iO')
        self.assertEqual(int_opt.default, i_dflt_val)
        self.assertEqual(int_opt.desc, i_desc)

        #                        name    valType     default    desc
        i_check = my_optz.add_option('iO', ValType.INT, i_dflt_val, i_desc)
        self.assertEqual(len(my_optz), 4)
        self.assertEqual(int_opt, i_check)

        # lists -----------------------------------------------------

        size_val = self.rng.next_int16()
        # select polarity of size randomly
        if self.rng.next_boolean():
            size_val = - size_val
        l_desc = "chunky"

        list_opt = ListOption('lO', default=size_val, desc=l_desc)
        self.assertEqual(list_opt.name, 'lO')
        self.assertEqual(list_opt.default, size_val)
        self.assertEqual(list_opt.size, size_val)
        self.assertEqual(list_opt.desc, l_desc)

        zero_val = 0
        var_list_opt = ListOption('zO', default=zero_val, desc="skinny")
        self.assertEqual(var_list_opt.name, 'zO')
        self.assertEqual(var_list_opt.default, zero_val)
        self.assertEqual(var_list_opt.desc, "skinny")

        #                        name    valType     default    desc
        l_check = my_optz.add_option('lO', ValType.LIST, size_val, l_desc)
        self.assertEqual(len(my_optz), 5)
        self.assertEqual(list_opt, l_check)

        # strings ---------------------------------------------------

        s_dflt_val = self.rng.next_file_name(12)
        s_desc = "wiggly"

        str_opt = StrOption('sO', default=s_dflt_val, desc=s_desc)
        self.assertEqual(str_opt.name, 'sO')
        self.assertEqual(str_opt.default, s_dflt_val)
        self.assertEqual(str_opt.desc, s_desc)

        #                        name    valType     default    desc
        s_check = my_optz.add_option('sO', ValType.STR, s_dflt_val, s_desc)
        self.assertEqual(len(my_optz), 6)
        self.assertEqual(str_opt, s_check)
Esempio n. 18
0
class TestPopulateDataDir(unittest.TestCase):
    """
    Test using a BuildList and existing content-keyed store to populate
    a data directory.
    """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################

    def make_unique(self, below):
        """ create a unique subdirectory of the directory named """

        dir_path = os.path.join(below, self.rng.next_file_name(8))
        while os.path.exists(dir_path):
            dir_path = os.path.join(below, self.rng.next_file_name(8))
        os.makedirs(dir_path, mode=0o755)
        return dir_path

    # actual unit tests #############################################

    def do_pop_test(self, hashtype):
        """ Test populating a data directory for a specific hashtype. """

        check_hashtype(hashtype)
        # DEBUG
        # print("do_pop_test: %s" % hashtype)
        # EMD

        sk_priv = RSA.generate(1024)
        sk_ = sk_priv.publickey()

        if hashtype == HashTypes.SHA1:
            original_data = os.path.join('example1', 'dataDir')
            original_u = os.path.join('example1', 'uDir')
        elif hashtype == HashTypes.SHA2:
            original_data = os.path.join('example2', 'dataDir')
            original_u = os.path.join('example2', 'uDir')
        elif hashtype == HashTypes.SHA3:
            original_data = os.path.join('example3', 'data_dir')
            original_u = os.path.join('example3', 'uDir')

        blist = BuildList.create_from_file_system(
            'name_of_the_list', original_data, sk_, hashtype=hashtype)

        # should return an empty list: a basic sanity check
        unmatched = blist.check_in_data_dir(original_data)
        # DEBUG
        # print("UNMATCHED IN DATA DIR: ", unmatched)
        # if len(unmatched) > 0:
        #   print("BL:\n%s" % blist.__str__())
        #   print("in the buildlist, but not in uData:")
        #   for un in unmatched:
        #       print("    %s %s" % (un[1], un[0]))
        # END
        self.assertEqual(len(unmatched), 0)

        # should return an empty list: a basic sanity check
        unmatched = blist.check_in_u_dir(original_u)
        # DEBUG
        # print("UNMATCHED IN U DIR: ", unmatched)
        if unmatched:
            print("BL:\n%s" % blist.__str__())
            print("in the buildlist, but not in u_dir:")
            for unm in unmatched:
                print("    %s %s" % (unm[1], unm[0]))
        # END
        self.assertEqual(len(unmatched), 0)

        self.assertEqual(blist.title, 'name_of_the_list')
        self.assertEqual(blist.public_key, sk_)
        self.assertEqual(blist.timestamp, timestamp(0))
        self.assertEqual(blist.hashtype, hashtype)

        self.assertEqual(blist, blist)
        self.assertFalse(blist.verify())   # not signed yet

        blist.sign(sk_priv)
        sig = blist.dig_sig                 # this is the base64-encoded value
        self.assertTrue(sig is not None)
        self.assertTrue(blist.verify())    # it has been signed

        self.assertEqual(blist, blist)

        # BL2: we build testDir and the new dataDir and u_dir --------
        string = blist.to_string()
        bl2 = BuildList.parse(string, hashtype)     # round-tripped build list
        # DEBUG
        # print("\nFIRST BUILD LIST:\n%s" % blist)
        # print("\nSECOND BUILD LIST:\n%s" % bl2)
        # END

        # string2 = bl2.__str__()
        # self.assertEqual(string, string2)
        # same list, but signed now
        self.assertEqual(blist, blist)
        # self.assertEqual(bl, bl2)               # timestamps may differ

        # create empty test directories -------------------
        test_path = self.make_unique('tmp')
        u_path = os.path.join(test_path, 'uDir')
        UDir.discover(
            u_path, hashtype=hashtype)  # creates empty UDir
        dvcz_path = os.path.join(test_path, 'dvcz')
        os.mkdir(dvcz_path)

        data_path = os.path.join(test_path, blist.tree.name)
        # DEBUG
        # print("DATA_PATH: %s" % data_path)
        # print("DVCZ_DIR:  %s" % dvczPath)
        # print("U_PATH:    %s" % u_path)
        # END

        # populate the new dataDir and then the new u_dir --
        # bl2.populateDataDir(originalU, data_path)
        blist.populate_data_dir(original_u, data_path)
        self.assertEqual(len(bl2.check_in_data_dir(data_path)), 0)

        bl2.tree.save_to_u_dir(data_path, u_path, hashtype)
        self.assertEqual(len(bl2.check_in_u_dir(u_path)), 0)

        # BL3:

        # this writes the buildlist to dvczPath/lastBuildList:
        blist3 = BuildList.list_gen("title", data_path, dvcz_path,
                                    u_path=u_path, hashtype=hashtype)
        path_to_list = os.path.join(dvcz_path, 'lastBuildList')
        with open(path_to_list, 'r') as file:
            ser4 = file.read()
        bl4 = BuildList.parse(ser4, hashtype)
        # ser41 = bl4.to_string()
        bl4.to_string()
        # self.assertEqual(ser41, ser4) # FAILS: ser41 is signed, ser4 isn't

        # DEBUG
        # print("recovered from disk:\n%s" % ser4)
        # print("\nserialized from BuildList:\n%s" % ser41)
        # END

        self.assertEqual(blist.tree, blist.tree)    # check __eq__
        self.assertEqual(bl2.tree, blist.tree)
        self.assertEqual(blist3.tree, blist.tree)
        self.assertEqual(bl4.tree, blist.tree)

    def test_populate_data_dir(self):
        """
        Test populate_data_dir for the supported hashtypes.
        """
        for hashtype in [HashTypes.SHA1, HashTypes.SHA2]:
            self.do_pop_test(hashtype)

    def test_name_space(self):
        """
        Verify that ArgumentParser works as expected, specifically
        that assignment adds a key to the Namespace.
        """
        parser = ArgumentParser(description='oh hello')
        args = parser.parse_args()
        args._ = 'trash'
        self.assertEqual(args._, 'trash')
Esempio n. 19
0
class TestMerkleDoc(unittest.TestCase):
    """ Test Merkletree functionality at the Document level. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################
    def get_two_unique_directory_names(self):
        """ Generate two quasi-random directory names. """

        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        self.assertTrue(len(dir_name1) > 0)
        self.assertTrue(len(dir_name2) > 0)
        self.assertTrue(dir_name1 != dir_name2)
        return (dir_name1, dir_name2)

    def make_one_named_test_directory(self, name, depth, width):
        """
        Create a randomly named directory under tmp/, removing any
        existing directory of that name.
        """

        dir_path = "tmp/%s" % name
        if os.path.exists(dir_path):
            if os.path.isfile(dir_path):
                os.unlink(dir_path)
            elif os.path.isdir(dir_path):
                shutil.rmtree(dir_path)
        self.rng.next_data_dir(dir_path, depth, width, 32)
        return dir_path

    def make_two_test_directories(self, depth, width):
        """
        Generate two different names, using them to create subdirectories
        of tmp/.
        """
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path1 = self.make_one_named_test_directory(dir_name1, depth, width)

        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path2 = self.make_one_named_test_directory(dir_name2, depth, width)

        return (dir_name1, dir_path1, dir_name2, dir_path2)

    def verify_leaf_hash(self, node, path_to_file, hashtype):
        """
        Verify that a MerkleLeaf correctly describes a file, given a hash type.
        """
        check_hashtype(hashtype)
        self.assertTrue(os.path.exists(path_to_file))
        with open(path_to_file, "rb") as file:
            data = file.read()
        self.assertFalse(data is None)
        if hashtype == HashTypes.SHA1:
            sha = XLSHA1()
        elif hashtype == HashTypes.SHA2:
            sha = XLSHA2()
        elif hashtype == HashTypes.SHA3:
            # pylint: disable=no-member
            sha = XLSHA3()
        elif hashtype == HashTypes.BLAKE2B_256:
            # pylint: disable=no-member
            sha = XLBLAKE2B_256()
        else:
            raise NotImplementedError
        sha.update(data)
        hash_ = sha.digest()
        self.assertEqual(hash_, node.bin_hash)

    def verify_tree_hash(self, node, path_to_tree, hashtype):
        """
        Given a MerkleTree, verify that it correctly describes the
        directory whose path is passed.
        """
        # we assume that the node is a MerkleTree
        check_hashtype(hashtype)
        if node.nodes is None:
            self.assertEqual(None, node.bin_hash)
        else:
            hash_count = 0
            if hashtype == HashTypes.SHA1:
                sha = XLSHA1()
            elif hashtype == HashTypes.SHA2:
                sha = XLSHA2()
            elif hashtype == HashTypes.SHA3:
                # pylint: disable=no-member
                sha = XLSHA3()
            elif hashtype == HashTypes.BLAKE2B_256:
                sha = XLBLAKE2B_256()
            else:
                raise NotImplementedError
            for node_ in node.nodes:
                path_to_node = os.path.join(path_to_tree, node_.name)
                if isinstance(node_, MerkleLeaf):
                    self.verify_leaf_hash(node_, path_to_node, hashtype)
                elif isinstance(node_, MerkleTree):
                    self.verify_tree_hash(node_, path_to_node, hashtype)
                else:
                    print("DEBUG: unknown node type!")
                    self.fail("unknown node type!")
                if node_.bin_hash is not None:
                    hash_count += 1
                    sha.update(node_.bin_hash)

            if hash_count == 0:
                self.assertEqual(None, node.bin_hash)
            else:
                self.assertEqual(sha.digest(), node.bin_hash)

    # actual unit tests #############################################

    def test_bound_flat_dirs(self):
        """test directory is single level, with four data files"""
        for hashtype in HashTypes:
            self.do_test_bound_flat_dirs(hashtype)

    def do_test_bound_flat_dirs(self, hashtype):
        """ Test two flat directories with the specified hash type. """

        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(ONE, FOUR)

        doc1 = MerkleDoc.create_from_file_system(dir_path1, hashtype)
        tree1 = doc1.tree
        self.assertTrue(isinstance(tree1, MerkleTree))
        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(FOUR, len(nodes1))
        self.verify_tree_hash(tree1, dir_path1, hashtype)

        doc2 = MerkleDoc.create_from_file_system(dir_path2, hashtype)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(FOUR, len(nodes2))
        self.verify_tree_hash(tree2, dir_path2, hashtype)

        self.assertEqual(tree1, tree1)
        self.assertFalse(tree1 == tree2)
        self.assertFalse(tree1 is None)

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str, hashtype)
        # DEBUG
        # print("flat doc:\n" + doc1Str)
        # print("rebuilt flat doc:\n" + doc1Rebuilt.toString())
        # END
        self.assertTrue(doc1 == doc1_rebuilt)

    def test_bound_needle_dirs(self):
        """test directories four deep with one data file at the lowest level"""
        for hashtype in HashTypes:
            self.do_test_bound_needle_dirs(hashtype)

    def do_test_bound_needle_dirs(self, hashtype):
        """ Run tests on two deeper directories. """
        check_hashtype(hashtype)
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(FOUR, ONE)
        doc1 = MerkleDoc.create_from_file_system(dir_path1, hashtype)
        tree1 = doc1.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(ONE, len(nodes1))
        self.verify_tree_hash(tree1, dir_path1, hashtype)

        doc2 = MerkleDoc.create_from_file_system(dir_path2, hashtype)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(ONE, len(nodes2))
        self.verify_tree_hash(tree2, dir_path2, hashtype)

        self.assertTrue(doc1 == doc1)
        self.assertFalse(doc1 == doc2)

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str, hashtype)
#       # DEBUG
#       print "needle doc:\n" + doc1Str
#       print "rebuilt needle doc:\n" + doc1Rebuilt.toString()
#       # END
        self.assertTrue(doc1 == doc1_rebuilt)
Esempio n. 20
0
class TestMerkleDoc(unittest.TestCase):

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################
    def get_two_unique_directory_names(self):
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        self.assertTrue(len(dir_name1) > 0)
        self.assertTrue(len(dir_name2) > 0)
        self.assertTrue(dir_name1 != dir_name2)
        return (dir_name1, dir_name2)

    def make_one_named_test_directory(self, name, depth, width):
        dir_path = "tmp/%s" % name
        if os.path.exists(dir_path):
            shutil.rmtree(dir_path)
        self.rng.next_data_dir(dir_path, depth, width, 32)
        return dir_path

    def make_two_test_directories(self, depth, width):
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path1 = self.make_one_named_test_directory(dir_name1, depth, width)

        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path2 = self.make_one_named_test_directory(dir_name2, depth, width)

        return (dir_name1, dir_path1, dir_name2, dir_path2)

    def verify_leaf_sha(self, node, path_to_file, hashtype):
        check_hashtype(hashtype)
        self.assertTrue(os.path.exists(path_to_file))
        with open(path_to_file, "rb") as file:
            data = file.read()
        self.assertFalse(data is None)
        # pylint: disable=redefined-variable-type
        if hashtype == HashTypes.SHA1:
            sha = hashlib.sha1()
        elif hashtype == HashTypes.SHA2:
            sha = hashlib.sha256()
        elif hashtype == HashTypes.SHA3:
            # pylint: disable=no-member
            sha = hashlib.sha3_256()
        sha.update(data)
        hash_ = sha.digest()
        self.assertEqual(hash_, node.bin_hash)

    def verify_tree_sha(self, node, path_to_tree, hashtype):
        # we assume that the node is a MerkleTree
        check_hashtype(hashtype)
        if node.nodes is None:
            self.assertEqual(None, node.bin_hash)
        else:
            hash_count = 0
            # pylint: disable=redefined-variable-type
            if hashtype == HashTypes.SHA1:
                sha = hashlib.sha1()
            elif hashtype == HashTypes.SHA2:
                sha = hashlib.sha256()
            elif hashtype == HashTypes.SHA3:
                # pylint: disable=no-member
                sha = hashlib.sha3_256()
            for node_ in node.nodes:
                path_to_node = os.path.join(path_to_tree, node_.name)
                if isinstance(node_, MerkleLeaf):
                    self.verify_leaf_sha(node_, path_to_node, hashtype)
                elif isinstance(node_, MerkleTree):
                    self.verify_tree_sha(node_, path_to_node, hashtype)
                else:
                    print("DEBUG: unknown node type!")
                    self.fail("unknown node type!")
                if node_.bin_hash is not None:
                    hash_count += 1
                    sha.update(node_.bin_hash)

            if hash_count == 0:
                self.assertEqual(None, node.bin_hash)
            else:
                self.assertEqual(sha.digest(), node.bin_hash)

    # actual unit tests #############################################

    def test_bound_flat_dirs(self):
        """test directory is single level, with four data files"""
        for using in [HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3, ]:
            self.do_test_bound_flat_dirs(using)

    def do_test_bound_flat_dirs(self, hashtype):

        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(ONE, FOUR)

        doc1 = MerkleDoc.create_from_file_system(dir_path1, hashtype)
        tree1 = doc1.tree
        self.assertTrue(isinstance(tree1, MerkleTree))
        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(FOUR, len(nodes1))
        self.verify_tree_sha(tree1, dir_path1, hashtype)

        doc2 = MerkleDoc.create_from_file_system(dir_path2, hashtype)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(FOUR, len(nodes2))
        self.verify_tree_sha(tree2, dir_path2, hashtype)

        self.assertEqual(tree1, tree1)
        self.assertFalse(tree1 == tree2)
        self.assertFalse(tree1 is None)

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str, hashtype)
        # DEBUG
        #print("flat doc:\n" + doc1Str)
        #print("rebuilt flat doc:\n" + doc1Rebuilt.toString())
        # END
        self.assertTrue(doc1.equal(doc1_rebuilt))  # MANGO

    def test_bound_needle_dirs(self):
        """test directories four deep with one data file at the lowest level"""
        for using in [HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3, ]:
            self.do_test_bound_needle_dirs(using)

    def do_test_bound_needle_dirs(self, hashtype):
        check_hashtype(hashtype)
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(FOUR, ONE)
        doc1 = MerkleDoc.create_from_file_system(dir_path1, hashtype)
        tree1 = doc1.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(ONE, len(nodes1))
        self.verify_tree_sha(tree1, dir_path1, hashtype)

        doc2 = MerkleDoc.create_from_file_system(dir_path2, hashtype)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(ONE, len(nodes2))
        self.verify_tree_sha(tree2, dir_path2, hashtype)

        self.assertTrue(doc1.equal(doc1))
        self.assertFalse(doc1.equal(doc2))

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str, hashtype)
#       # DEBUG
#       print "needle doc:\n" + doc1Str
#       print "rebuilt needle doc:\n" + doc1Rebuilt.toString()
#       # END
        self.assertTrue(doc1.equal(doc1_rebuilt))       # FOO
Esempio n. 21
0
class TestMerkleTree(unittest.TestCase):
    """ Test package functionality at the Tree level. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions ---------------------------------------------

    def get_two_unique_directory_names(self):
        """ Make two different quasi-random directory names."""
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        self.assertTrue(len(dir_name1) > 0)
        self.assertTrue(len(dir_name2) > 0)
        self.assertTrue(dir_name1 != dir_name2)
        return (dir_name1, dir_name2)

    def make_one_named_test_directory(self, name, depth, width):
        """ Make a directory tree with a specific name, depth and width."""
        dir_path = "tmp/%s" % name
        if os.path.exists(dir_path):
            if os.path.isfile(dir_path):
                os.unlink(dir_path)
            elif os.path.isdir(dir_path):
                shutil.rmtree(dir_path)
        self.rng.next_data_dir(dir_path, depth, width, 32)
        return dir_path

    def make_two_test_directories(self, depth, width):
        """ Create two test directories with different names. """
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path1 = self.make_one_named_test_directory(dir_name1, depth, width)

        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path2 = self.make_one_named_test_directory(dir_name2, depth, width)

        return (dir_name1, dir_path1, dir_name2, dir_path2)

    def verify_leaf_sha(self, node, path_to_file, hashtype):
        """
        Verify a leaf node is hashed correctly, using a specific SHA hash type.
        """
        self.assertTrue(os.path.exists(path_to_file))
        with open(path_to_file, "rb") as file:
            data = file.read()
        self.assertFalse(data is None)
        if hashtype == HashTypes.SHA1:
            sha = XLSHA1()
        elif hashtype == HashTypes.SHA2:
            sha = XLSHA2()
        elif hashtype == HashTypes.SHA3:
            sha = XLSHA3()
        elif hashtype == HashTypes.BLAKE2B:
            sha = XLBLAKE2B_256()
        else:
            raise NotImplementedError
        sha.update(data)
        hash_ = sha.digest()
        self.assertEqual(hash_, node.bin_hash)

    def verify_tree_sha(self, node, path_to_node, hashtype):
        """
        Verify tree elements are hashed correctly, assuming that the node
        is a MerkleTree, using a specific SHA hash type.
        """
        if node.nodes is None:
            self.assertEqual(None, node.bin_hash)
        else:
            hash_count = 0
            if hashtype == HashTypes.SHA1:
                sha = XLSHA1()
            elif hashtype == HashTypes.SHA2:
                sha = XLSHA2()
            elif hashtype == HashTypes.SHA3:
                sha = XLSHA3()
            elif hashtype == HashTypes.BLAKE2B:
                sha = XLBLAKE2B_256()
            else:
                raise NotImplementedError
            for node_ in node.nodes:
                path_to_file = os.path.join(path_to_node, node_.name)
                if isinstance(node_, MerkleLeaf):
                    self.verify_leaf_sha(node_, path_to_file, hashtype)
                elif isinstance(node_, MerkleTree):
                    self.verify_tree_sha(node_, path_to_file, hashtype)
                else:
                    self.fail("unknown node type!")
                if node_.bin_hash is not None:
                    hash_count += 1
                    sha.update(node_.bin_hash)

            # take care to compare values of the same type;
            # node.binHash is binary, node.hexHash is hex
            if hash_count == 0:
                self.assertEqual(None, node.bin_hash)
            else:
                self.assertEqual(sha.digest(), node.bin_hash)

    # unit tests ----------------------------------------------------

    def test_pathless_unbound(self):
        """
        Test basic characteristics of very simple MerkleTrees created
        using our standard SHA hash types.
        """
        for using in [HashTypes.SHA1, HashTypes.SHA2,
                      HashTypes.SHA3, HashTypes.BLAKE2B]:
            self.do_test_pathless_unbound(using)

    def do_test_pathless_unbound(self, hashtype):
        """
        Test basic characteristics of very simple MerkleTrees created
        using a specific SHA hash type.
        """
        (dir_name1, dir_name2) = self.get_two_unique_directory_names()

        check_hashtype(hashtype)
        tree1 = MerkleTree(dir_name1, hashtype)
        self.assertEqual(dir_name1, tree1.name)
        if hashtype == HashTypes.SHA1:
            self.assertEqual(SHA1_HEX_NONE, tree1.hex_hash)
        elif hashtype == HashTypes.SHA2:
            self.assertEqual(SHA2_HEX_NONE, tree1.hex_hash)
        elif hashtype == HashTypes.SHA3:
            self.assertEqual(SHA3_HEX_NONE, tree1.hex_hash)
        elif hashtype == HashTypes.BLAKE2B_256:
            self.assertEqual(BLAKE2B_256_HEX_NONE, tree1.hex_hash)
        else:
            raise NotImplementedError
        tree2 = MerkleTree(dir_name2, hashtype)
        self.assertEqual(dir_name2, tree2.name)

        # these tests remain skimpy
        self.assertFalse(tree1 is None)
        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)

        tree1_str = tree1.to_string(0)

        # there should be no indent on the first line
        self.assertFalse(tree1_str[0] == ' ')

        # no extra lines should be added
        lines = tree1_str.split('\n')
        # this split generates an extra blank line, because the serialization
        # ends with CR-LF
        if lines[-1] == '':
            lines = lines[:-1]
        self.assertEqual(1, len(lines))

        tree1_rebuilt = MerkleTree.create_from_serialization(
            tree1_str, hashtype)
        self.assertTrue(tree1 == tree1_rebuilt)

    def test_bound_flat_dirs(self):
        """
        Test handling of flat directories with a few data files
        using varioush SHA hash types.
        """
        for using in [HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3, ]:
            self.do_test_bound_flat_dirs(using)

    def do_test_bound_flat_dirs(self, hashtype):
        """test directory is single level, with four data files"""

        check_hashtype(hashtype)
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(ONE, FOUR)
        tree1 = MerkleTree.create_from_file_system(dir_path1, hashtype)
        self.assertEqual(dir_name1, tree1.name)
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(FOUR, len(nodes1))
        self.verify_tree_sha(tree1, dir_path1, hashtype)

        tree2 = MerkleTree.create_from_file_system(dir_path2, hashtype)
        self.assertEqual(dir_name2, tree2.name)
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(FOUR, len(nodes2))
        self.verify_tree_sha(tree2, dir_path2, hashtype)

        self.assertFalse(tree1 is None)
        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)

        tree1_str = tree1.to_string(0)
        tree1_rebuilt = MerkleTree.create_from_serialization(
            tree1_str, hashtype)
        self.assertTrue(tree1 == tree1_rebuilt)

    def test_bound_needle_dirs(self):
        """
        Test directories four deep with various SHA hash types.
        """
        for using in [HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3, ]:
            self.do_test_bound_needle_dirs(using)

    def do_test_bound_needle_dirs(self, hashtype):
        """test directories four deep with one data file at the lowest level"""
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(FOUR, ONE)
        tree1 = MerkleTree.create_from_file_system(dir_path1, hashtype)

        self.assertEqual(dir_name1, tree1.name)
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(ONE, len(nodes1))
        self.verify_tree_sha(tree1, dir_path1, hashtype)

        tree2 = MerkleTree.create_from_file_system(dir_path2, hashtype)
        self.assertEqual(dir_name2, tree2.name)
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(ONE, len(nodes2))
        self.verify_tree_sha(tree2, dir_path2, hashtype)

        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)

        tree1_str = tree1.to_string(0)
        tree1_rebuilt = MerkleTree.create_from_serialization(
            tree1_str, hashtype)
#       # DEBUG
#       print "NEEDLEDIR TREE1:\n" + tree1Str
#       print "REBUILT TREE1:\n" + tree1Rebuilt.toString("")
#       # END
        self.assertTrue(tree1 == tree1_rebuilt)

    # tests of bugs previously found --------------------------------

    def test_gray_boxes_bug1(self):
        """
        Verify that bug #1 in handling serialization of grayboxes website
        has been corrected.
        """
        serialization =\
            '721a08022dd26e7be98b723f26131786fd2c0dc3 grayboxes.com/\n' +\
            ' fcd3973c66230b9078a86a5642b4c359fe72d7da images/\n' +\
            '  15e47f4eb55197e1bfffae897e9d5ce4cba49623 grayboxes.gif\n' +\
            ' 2477b9ea649f3f30c6ed0aebacfa32cb8250f3df index.html\n'

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(4, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA1)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(
            serialization, HashTypes.SHA1)

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        self.assertTrue(tree1 == tree2)

        # 2014-06-26 tagged this on here to test firstLineRE_1()
        first_line = string[0]
        match_ = MerkleTree.first_line_re_1().match(first_line)
        self.assertTrue(match_ is not None)
        self.assertEqual(match_.group(1), '')               # indent
        tree_hash = match_.group(2)
        dir_name = match_.group(3)
        self.assertEqual(tree_hash + ' ' + dir_name, first_line)

    def test_xlattice_bug1(self):
        """
        this test relies on dat.xlattice.org being locally present
        and an internally consistent merkleization
        """
        with open('tests/test_data/dat.xlattice.org', 'rb') as file:
            serialization = str(file.read(), 'utf-8')

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(
            serialization, HashTypes.SHA1)

#       # DEBUG
#       print "tree1 has %d nodes" % len(tree1.nodes)
#       with open('junk.tree1', 'w') as t:
#           t.write( tree1.toString(0) )
#       # END

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(2511, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA1)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        self.assertTrue(tree1 == tree2)

    def test_gray_boxes_bug3(self):
        """ Test solution to bug in handling grayboxes website. """

        serialization =\
            '088d0e391e1a4872329e0f7ac5d45b2025363e26c199a7' + \
            '4ea39901d109afd6ba grayboxes.com/\n' +\
            ' 24652ddc14687866e6b1251589aee7e1e3079a87f80cd' + \
            '7775214f6d837612a90 images/\n' +\
            '  1eb774eef9be1e696f69a2f95711be37915aac283bb4' + \
            'b34dcbaf7d032233e090 grayboxes.gif\n' +\
            ' 6eacebda9fd55b59c0d2e48e2ed59ce9fd683379592f8' + \
            'e662b1de88e041f53c9 index.html\n'

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(4, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA2)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(
            serialization, HashTypes.SHA2)

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        self.assertTrue(tree1 == tree2)

        # 2014-06-26 tagged this on here to test firstLineRE_1()
        first_line = string[0]
        match_ = MerkleTree.first_line_re_2().match(first_line)
        self.assertTrue(match_ is not None)
        self.assertEqual(match_.group(1), '')               # indent
        tree_hash = match_.group(2)
        dir_name = match_.group(3)
        self.assertEqual(tree_hash + ' ' + dir_name, first_line)

    def test_xlattice_bug3(self):
        """
        this test relies on dat2.xlattice.org being locally present
        and an internally consistent merkleization
        """
        with open('tests/test_data/dat2.xlattice.org', 'rb') as file:
            serialization = str(file.read(), 'utf-8')

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(
            serialization, HashTypes.SHA2)

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(2511, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA2)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        self.assertTrue(tree1 == tree2)
Esempio n. 22
0
class TestNLHTree(unittest.TestCase):
    """ Test NLHTree-related functions. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions #############################################
    def make_leaf(self, names_so_far, hashtype):
        """ Build a leaf with random name and data using specific hash. """

        while True:
            name = self.rng.next_file_name(8)
            if name not in names_so_far:
                names_so_far.add(name)
                break
        nnn = self.rng.some_bytes(8)        # 8 quasi-random bytes
        if hashtype == HashTypes.SHA1:
            sha = hashlib.sha1()
        elif hashtype == HashTypes.SHA2:
            sha = hashlib.sha256()
        elif hashtype == HashTypes.SHA3:
            sha = hashlib.sha3_256()
        sha.update(nnn)
        return NLHLeaf(name, sha.digest(), hashtype)

    # actual unit tests #############################################
    def test_simple_constructor(self):
        """ Build a tree with random name and data using various hashes. """

        for using in [HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3, ]:
            self.do_test_simple_constructor(using)

    def do_test_simple_constructor(self, hashtype):
        """
        Build a tree with random name and data using specific hash type.
        """

        name = self.rng.next_file_name(8)
        tree = NLHTree(name, hashtype)
        self.assertEqual(tree.name, name)
        self.assertEqual(tree.hashtype, hashtype)
        self.assertEqual(len(tree.nodes), 0)

    def do_test_insert_4_leafs(self, hashtype):
        """
        Create 4 leaf nodes with random but unique names.  Insert
        them into a tree, verifying that the resulting sort is correct.
        """
        check_hashtype(hashtype)
        name = self.rng.next_file_name(8)
        tree = NLHTree(name, hashtype)
        leaf_names = set()
        a_leaf = self.make_leaf(leaf_names, hashtype)
        b_leaf = self.make_leaf(leaf_names, hashtype)
        c_leaf = self.make_leaf(leaf_names, hashtype)
        d_leaf = self.make_leaf(leaf_names, hashtype)
        self.assertEqual(len(tree.nodes), 0)
        tree.insert(a_leaf)
        self.assertEqual(len(tree.nodes), 1)
        tree.insert(b_leaf)
        self.assertEqual(len(tree.nodes), 2)
        tree.insert(c_leaf)
        self.assertEqual(len(tree.nodes), 3)
        tree.insert(d_leaf)
        self.assertEqual(len(tree.nodes), 4)
        # we expect the nodes to be sorted
        for ndx in range(3):
            self.assertTrue(tree.nodes[ndx].name < tree.nodes[ndx + 1].name)

        matches = tree.list('*')
        for ndx, qqq in enumerate(tree.nodes):
            self.assertEqual(matches[ndx], '  ' + qqq.name)

        self.assertEqual(tree, tree)
        tree2 = tree.clone()
        self.assertEqual(tree2, tree)

    def test_insert_4_leafs(self):
        """
        Test inserting 4 leafs into a tree using various hash types.
        """
        for using in [HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3, ]:
            self.do_test_insert_4_leafs(using)
Esempio n. 23
0
class TestMerkleTree(unittest.TestCase):
    """ Test package functionality at the Tree level. """
    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    # utility functions ---------------------------------------------

    def get_two_unique_directory_names(self):
        """ Make two different quasi-random directory names."""
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        self.assertTrue(len(dir_name1) > 0)
        self.assertTrue(len(dir_name2) > 0)
        self.assertTrue(dir_name1 != dir_name2)
        return (dir_name1, dir_name2)

    def make_one_named_test_directory(self, name, depth, width):
        """ Make a directory tree with a specific name, depth and width."""
        dir_path = "tmp/%s" % name
        if os.path.exists(dir_path):
            if os.path.isfile(dir_path):
                os.unlink(dir_path)
            elif os.path.isdir(dir_path):
                shutil.rmtree(dir_path)
        self.rng.next_data_dir(dir_path, depth, width, 32)
        return dir_path

    def make_two_test_directories(self, depth, width):
        """ Create two test directories with different names. """
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path1 = self.make_one_named_test_directory(dir_name1, depth, width)

        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path2 = self.make_one_named_test_directory(dir_name2, depth, width)

        return (dir_name1, dir_path1, dir_name2, dir_path2)

    def verify_leaf_sha(self, node, path_to_file, hashtype):
        """
        Verify a leaf node is hashed correctly, using a specific SHA hash type.
        """
        self.assertTrue(os.path.exists(path_to_file))
        with open(path_to_file, "rb") as file:
            data = file.read()
        self.assertFalse(data is None)
        if hashtype == HashTypes.SHA1:
            sha = XLSHA1()
        elif hashtype == HashTypes.SHA2:
            sha = XLSHA2()
        elif hashtype == HashTypes.SHA3:
            sha = XLSHA3()
        elif hashtype == HashTypes.BLAKE2B:
            sha = XLBLAKE2B_256()
        else:
            raise NotImplementedError
        sha.update(data)
        hash_ = sha.digest()
        self.assertEqual(hash_, node.bin_hash)

    def verify_tree_sha(self, node, path_to_node, hashtype):
        """
        Verify tree elements are hashed correctly, assuming that the node
        is a MerkleTree, using a specific SHA hash type.
        """
        if node.nodes is None:
            self.assertEqual(None, node.bin_hash)
        else:
            hash_count = 0
            if hashtype == HashTypes.SHA1:
                sha = XLSHA1()
            elif hashtype == HashTypes.SHA2:
                sha = XLSHA2()
            elif hashtype == HashTypes.SHA3:
                sha = XLSHA3()
            elif hashtype == HashTypes.BLAKE2B:
                sha = XLBLAKE2B_256()
            else:
                raise NotImplementedError
            for node_ in node.nodes:
                path_to_file = os.path.join(path_to_node, node_.name)
                if isinstance(node_, MerkleLeaf):
                    self.verify_leaf_sha(node_, path_to_file, hashtype)
                elif isinstance(node_, MerkleTree):
                    self.verify_tree_sha(node_, path_to_file, hashtype)
                else:
                    self.fail("unknown node type!")
                if node_.bin_hash is not None:
                    hash_count += 1
                    sha.update(node_.bin_hash)

            # take care to compare values of the same type;
            # node.binHash is binary, node.hexHash is hex
            if hash_count == 0:
                self.assertEqual(None, node.bin_hash)
            else:
                self.assertEqual(sha.digest(), node.bin_hash)

    # unit tests ----------------------------------------------------

    def test_pathless_unbound(self):
        """
        Test basic characteristics of very simple MerkleTrees created
        using our standard SHA hash types.
        """
        for using in [
                HashTypes.SHA1, HashTypes.SHA2, HashTypes.SHA3,
                HashTypes.BLAKE2B
        ]:
            self.do_test_pathless_unbound(using)

    def do_test_pathless_unbound(self, hashtype):
        """
        Test basic characteristics of very simple MerkleTrees created
        using a specific SHA hash type.
        """
        (dir_name1, dir_name2) = self.get_two_unique_directory_names()

        check_hashtype(hashtype)
        tree1 = MerkleTree(dir_name1, hashtype)
        self.assertEqual(dir_name1, tree1.name)
        if hashtype == HashTypes.SHA1:
            self.assertEqual(SHA1_HEX_NONE, tree1.hex_hash)
        elif hashtype == HashTypes.SHA2:
            self.assertEqual(SHA2_HEX_NONE, tree1.hex_hash)
        elif hashtype == HashTypes.SHA3:
            self.assertEqual(SHA3_HEX_NONE, tree1.hex_hash)
        elif hashtype == HashTypes.BLAKE2B_256:
            self.assertEqual(BLAKE2B_256_HEX_NONE, tree1.hex_hash)
        else:
            raise NotImplementedError
        tree2 = MerkleTree(dir_name2, hashtype)
        self.assertEqual(dir_name2, tree2.name)

        # these tests remain skimpy
        self.assertFalse(tree1 is None)
        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)

        tree1_str = tree1.to_string(0)

        # there should be no indent on the first line
        self.assertFalse(tree1_str[0] == ' ')

        # no extra lines should be added
        lines = tree1_str.split('\n')
        # this split generates an extra blank line, because the serialization
        # ends with CR-LF
        if lines[-1] == '':
            lines = lines[:-1]
        self.assertEqual(1, len(lines))

        tree1_rebuilt = MerkleTree.create_from_serialization(
            tree1_str, hashtype)
        self.assertTrue(tree1 == tree1_rebuilt)

    def test_bound_flat_dirs(self):
        """
        Test handling of flat directories with a few data files
        using varioush SHA hash types.
        """
        for using in [
                HashTypes.SHA1,
                HashTypes.SHA2,
                HashTypes.SHA3,
        ]:
            self.do_test_bound_flat_dirs(using)

    def do_test_bound_flat_dirs(self, hashtype):
        """test directory is single level, with four data files"""

        check_hashtype(hashtype)
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(ONE, FOUR)
        tree1 = MerkleTree.create_from_file_system(dir_path1, hashtype)
        self.assertEqual(dir_name1, tree1.name)
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(FOUR, len(nodes1))
        self.verify_tree_sha(tree1, dir_path1, hashtype)

        tree2 = MerkleTree.create_from_file_system(dir_path2, hashtype)
        self.assertEqual(dir_name2, tree2.name)
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(FOUR, len(nodes2))
        self.verify_tree_sha(tree2, dir_path2, hashtype)

        self.assertFalse(tree1 is None)
        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)

        tree1_str = tree1.to_string(0)
        tree1_rebuilt = MerkleTree.create_from_serialization(
            tree1_str, hashtype)
        self.assertTrue(tree1 == tree1_rebuilt)

    def test_bound_needle_dirs(self):
        """
        Test directories four deep with various SHA hash types.
        """
        for using in [
                HashTypes.SHA1,
                HashTypes.SHA2,
                HashTypes.SHA3,
        ]:
            self.do_test_bound_needle_dirs(using)

    def do_test_bound_needle_dirs(self, hashtype):
        """test directories four deep with one data file at the lowest level"""
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(FOUR, ONE)
        tree1 = MerkleTree.create_from_file_system(dir_path1, hashtype)

        self.assertEqual(dir_name1, tree1.name)
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(ONE, len(nodes1))
        self.verify_tree_sha(tree1, dir_path1, hashtype)

        tree2 = MerkleTree.create_from_file_system(dir_path2, hashtype)
        self.assertEqual(dir_name2, tree2.name)
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(ONE, len(nodes2))
        self.verify_tree_sha(tree2, dir_path2, hashtype)

        self.assertTrue(tree1 == tree1)
        self.assertFalse(tree1 == tree2)

        tree1_str = tree1.to_string(0)
        tree1_rebuilt = MerkleTree.create_from_serialization(
            tree1_str, hashtype)
        #       # DEBUG
        #       print "NEEDLEDIR TREE1:\n" + tree1Str
        #       print "REBUILT TREE1:\n" + tree1Rebuilt.toString("")
        #       # END
        self.assertTrue(tree1 == tree1_rebuilt)

    # tests of bugs previously found --------------------------------

    def test_gray_boxes_bug1(self):
        """
        Verify that bug #1 in handling serialization of grayboxes website
        has been corrected.
        """
        serialization =\
            '721a08022dd26e7be98b723f26131786fd2c0dc3 grayboxes.com/\n' +\
            ' fcd3973c66230b9078a86a5642b4c359fe72d7da images/\n' +\
            '  15e47f4eb55197e1bfffae897e9d5ce4cba49623 grayboxes.gif\n' +\
            ' 2477b9ea649f3f30c6ed0aebacfa32cb8250f3df index.html\n'

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(4, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA1)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(serialization,
                                                     HashTypes.SHA1)

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        self.assertTrue(tree1 == tree2)

        # 2014-06-26 tagged this on here to test firstLineRE_1()
        first_line = string[0]
        match_ = MerkleTree.first_line_re_1().match(first_line)
        self.assertTrue(match_ is not None)
        self.assertEqual(match_.group(1), '')  # indent
        tree_hash = match_.group(2)
        dir_name = match_.group(3)
        self.assertEqual(tree_hash + ' ' + dir_name, first_line)

    def test_xlattice_bug1(self):
        """
        this test relies on dat.xlattice.org being locally present
        and an internally consistent merkleization
        """
        with open('tests/test_data/dat.xlattice.org', 'rb') as file:
            serialization = str(file.read(), 'utf-8')

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(serialization,
                                                     HashTypes.SHA1)

        #       # DEBUG
        #       print "tree1 has %d nodes" % len(tree1.nodes)
        #       with open('junk.tree1', 'w') as t:
        #           t.write( tree1.toString(0) )
        #       # END

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(2511, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA1)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        self.assertTrue(tree1 == tree2)

    def test_gray_boxes_bug3(self):
        """ Test solution to bug in handling grayboxes website. """

        serialization =\
            '088d0e391e1a4872329e0f7ac5d45b2025363e26c199a7' + \
            '4ea39901d109afd6ba grayboxes.com/\n' +\
            ' 24652ddc14687866e6b1251589aee7e1e3079a87f80cd' + \
            '7775214f6d837612a90 images/\n' +\
            '  1eb774eef9be1e696f69a2f95711be37915aac283bb4' + \
            'b34dcbaf7d032233e090 grayboxes.gif\n' +\
            ' 6eacebda9fd55b59c0d2e48e2ed59ce9fd683379592f8' + \
            'e662b1de88e041f53c9 index.html\n'

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(4, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA2)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(serialization,
                                                     HashTypes.SHA2)

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        self.assertTrue(tree1 == tree2)

        # 2014-06-26 tagged this on here to test firstLineRE_1()
        first_line = string[0]
        match_ = MerkleTree.first_line_re_2().match(first_line)
        self.assertTrue(match_ is not None)
        self.assertEqual(match_.group(1), '')  # indent
        tree_hash = match_.group(2)
        dir_name = match_.group(3)
        self.assertEqual(tree_hash + ' ' + dir_name, first_line)

    def test_xlattice_bug3(self):
        """
        this test relies on dat2.xlattice.org being locally present
        and an internally consistent merkleization
        """
        with open('tests/test_data/dat2.xlattice.org', 'rb') as file:
            serialization = str(file.read(), 'utf-8')

        # create from serialization ---------------------------------
        tree1 = MerkleTree.create_from_serialization(serialization,
                                                     HashTypes.SHA2)

        ser1 = tree1.to_string(0)
        self.assertEqual(serialization, ser1)

        # create from string array ----------------------------------
        string = serialization.split('\n')
        string = string[:-1]
        self.assertEqual(2511, len(string))

        tree2 = MerkleTree.create_from_string_array(string, HashTypes.SHA2)

        ser2 = tree2.to_string(0)
        self.assertEqual(serialization, ser2)

        self.assertTrue(tree1 == tree2)
Esempio n. 24
0
class TestMerkleDoc(unittest.TestCase):
    """ Test MerkleTree functionality at the document level. """

    def setUp(self):
        self.rng = SimpleRNG(time.time())

    def tearDown(self):
        pass

    def get_two_unique_directory_names(self):
        """
        Get two candidate directory names, making sure that they differ.
        """
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        self.assertTrue(len(dir_name1) > 0)
        self.assertTrue(len(dir_name2) > 0)
        self.assertTrue(dir_name1 != dir_name2)
        return (dir_name1, dir_name2)

    def make_one_named_test_directory(self, name, depth, width):
        """
        Create a test directory with the name, depth, and width specified.
        The directory is under tmp/ ; subdirectories have random names
        and contents.
        """
        dir_path = "tmp/%s" % name
        if os.path.exists(dir_path):
            shutil.rmtree(dir_path)
        self.rng.next_data_dir(dir_path, depth, width, 32)
        return dir_path

    def make_two_test_directories(self, depth, width):
        """
        Create two test directories under tmp/ with distinct names but the
        depth and width specified.
        """
        dir_name1 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path1 = self.make_one_named_test_directory(dir_name1, depth, width)

        dir_name2 = dir_name1
        while dir_name2 == dir_name1:
            dir_name2 = self.rng.next_file_name(MAX_NAME_LEN)
        dir_path2 = self.make_one_named_test_directory(dir_name2, depth, width)

        return (dir_name1, dir_path1, dir_name2, dir_path2)

    def verify_leaf_sha256(self, node, path_to_file):
        """
        Verify that the content keys of the named file match the SHA
        hash of its contents.
        """
        self.assertTrue(os.path.exists(path_to_file))
        with open(path_to_file, "rb") as file:
            data = file.read()
        self.assertFalse(data is None)
        sha = XLSHA2()
        sha.update(data)
        hash_ = sha.digest()
        self.assertEqual(hash_, node.bin_hash)

    def verify_tree_sha256(self, node, path_to_tree):
        """
        Verify that the names (content keys) of files below the node
        (a Merkletree) have correct content keys, matching the SHA
        hash of the files.
        """
        if node.nodes is None:
            self.assertEqual(None, node.bin_hash)
        else:
            hash_count = 0
            sha = XLSHA2()
            for node_ in node.nodes:
                path_to_node = os.path.join(path_to_tree, node_.name)
                if isinstance(node_, MerkleLeaf):
                    self.verify_leaf_sha256(node_, path_to_node)
                elif isinstance(node_, MerkleTree):
                    self.verify_tree_sha256(node_, path_to_node)
                else:
                    print("DEBUG: unknown node type!")
                    self.fail("unknown node type!")
                if node_.bin_hash is not None:
                    hash_count += 1
                    sha.update(node_.bin_hash)

            if hash_count == 0:
                self.assertEqual(None, node.bin_hash)
            else:
                self.assertEqual(sha.digest(), node.bin_hash)

    # actual unit tests #############################################

    def test_bound_flat_dirs(self):
        """test directory is single level, with four data files"""

        dir_name1, dir_path1, dir_name2, dir_path2 = \
            self.make_two_test_directories(ONE, FOUR)
        doc1 = MerkleDoc.create_from_file_system(dir_path1)
        # pylint: disable=no-member
        tree1 = doc1.tree
        self.assertTrue(isinstance(tree1, MerkleTree))

        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(FOUR, len(nodes1))
        self.verify_tree_sha256(tree1, dir_path1)

        doc2 = MerkleDoc.create_from_file_system(dir_path2)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(FOUR, len(nodes2))
        self.verify_tree_sha256(tree2, dir_path2)

        # pylint: disable=no-member
        self.assertTrue(tree1 == tree1)
        # pylint: disable=no-member
        self.assertFalse(tree1 == tree2)
        # pylint: disable=no-member
        self.assertFalse(tree1 is None)

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str)
        self.assertTrue(doc1 == doc1_rebuilt)

    def test_bound_needle_dirs(self):
        """test directories four deep with one data file at the lowest level"""
        (dir_name1, dir_path1, dir_name2, dir_path2) =\
            self.make_two_test_directories(FOUR, ONE)
        doc1 = MerkleDoc.create_from_file_system(dir_path1)
        tree1 = doc1.tree
        self.assertTrue(isinstance(tree1, MerkleTree))

        # pylint: disable=no-member
        self.assertEqual(dir_name1, tree1.name)
        self.assertTrue(doc1.bound)
        self.assertEqual(("tmp/%s" % dir_name1), dir_path1)
        # pylint: disable=no-member
        nodes1 = tree1.nodes
        self.assertTrue(nodes1 is not None)
        self.assertEqual(ONE, len(nodes1))
        self.verify_tree_sha256(tree1, dir_path1)

        doc2 = MerkleDoc.create_from_file_system(dir_path2)
        tree2 = doc2.tree
        # pylint: disable=no-member
        self.assertEqual(dir_name2, tree2.name)
        self.assertTrue(doc2.bound)
        self.assertEqual(("tmp/%s" % dir_name2), dir_path2)
        # pylint: disable=no-member
        nodes2 = tree2.nodes
        self.assertTrue(nodes2 is not None)
        self.assertEqual(ONE, len(nodes2))
        self.verify_tree_sha256(tree2, dir_path2)

        self.assertTrue(doc1 == doc1)
        self.assertFalse(doc1 == doc2)

        doc1_str = doc1.to_string()
        doc1_rebuilt = MerkleDoc.create_from_serialization(doc1_str)
#       # DEBUG
#       print "needle doc:\n" + doc1Str
#       print "rebuilt needle doc:\n" + doc1Rebuilt.toString()
#       # END
        self.assertTrue(doc1 == doc1_rebuilt)       # FOO