diff --git a/.travis.yml b/.travis.yml
index 27c9f66..3bbfd69 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,7 +6,5 @@ python:
   - "3.7-dev"
   - "3.8-dev"
   - "nightly"
-install:
-  - pip install -r requirements.txt
 script:
-  - pytest
+  - python setup.py test
diff --git a/PA193_mnemonic_Slytherin/__init__.py b/PA193_mnemonic_Slytherin/__init__.py
index 2ad9ae6..2370251 100644
--- a/PA193_mnemonic_Slytherin/__init__.py
+++ b/PA193_mnemonic_Slytherin/__init__.py
@@ -11,3 +11,4 @@ Team Slytherin: @sobuch, @lsolodkova, @mvondracek.
 """
 from .mnemonic import do_some_work
 from .mnemonic import generate, recover, verify
+from .mnemonic import is_valid_entropy, is_valid_mnemonic, is_valid_seed
diff --git a/PA193_mnemonic_Slytherin/mnemonic.py b/PA193_mnemonic_Slytherin/mnemonic.py
index c5104fc..c69cc2e 100644
--- a/PA193_mnemonic_Slytherin/mnemonic.py
+++ b/PA193_mnemonic_Slytherin/mnemonic.py
@@ -58,25 +58,30 @@ def __mnemonic2entropy(mnemonic: str) -> bytes:
     pass
 
 
-def __is_valid_entropy(entropy: bytes) -> bool:
+def is_valid_entropy(entropy: bytes) -> bool:
     """Check whether provided bytes represent a valid entropy according to BIP39.
     https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
+
+    > The mnemonic must encode entropy in a multiple of 32 bits. With more entropy security is
+    > improved but the sentence length increases. We refer to the initial entropy length as ENT.
+    > The allowed size of ENT is 128-256 bits.
+    > https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic
     """
-    pass
+    return len(entropy) in list(range(16, 32+1, 4))
 
 
-def __is_valid_mnemonic(mnemonic: str) -> bool:
+def is_valid_mnemonic(mnemonic: str) -> bool:
     """Check whether provided string represents a valid mnemonic phrase based on current dictionary.
     Currently uses 1 default dictionary with English words.
     # TODO Should we support multiple dictionaries for various languages?
     """
-    pass
+    raise NotImplementedError()
 
 
-def __is_valid_seed(seed: bytes) -> bool:
+def is_valid_seed(seed: bytes) -> bool:
     """Check whether provided bytes represent a valid seed.
     """
-    pass
+    raise NotImplementedError()
 
 
 def _secure_seed_compare(expected_seed: bytes, actual_seed: bytes) -> bool:
diff --git a/PA193_mnemonic_Slytherin/mnemoniccli.py b/PA193_mnemonic_Slytherin/mnemoniccli.py
index 07c2c01..d47ece3 100644
--- a/PA193_mnemonic_Slytherin/mnemoniccli.py
+++ b/PA193_mnemonic_Slytherin/mnemoniccli.py
@@ -12,15 +12,16 @@ Team Slytherin: @sobuch, @lsolodkova, @mvondracek.
 """
 import argparse
 import logging
+import os
 import sys
 import warnings
+from binascii import unhexlify, hexlify
 from enum import Enum, unique
 from pprint import saferepr
+import typing
 from typing import Sequence
 
-import coloredlogs
-
-from PA193_mnemonic_Slytherin import do_some_work
+from PA193_mnemonic_Slytherin import generate, recover, verify, is_valid_entropy, is_valid_mnemonic, is_valid_seed
 
 __version__ = '0.1.0'
 __author__ = 'Team Slytherin: @sobuch, @lsolodkova, @mvondracek.'
@@ -43,12 +44,21 @@ class ExitCode(Enum):
     ARGUMENTS = 2
     """Incorrect or missing arguments provided."""
 
+    EX_DATAERR = 65
+    """The input data was incorrect in some way."""
+
+    EX_NOINPUT = 66
+    """An input file (not a system file) did not exist or was not readable."""
+
     EX_UNAVAILABLE = 69
     """Required program or file does not exist."""
 
     EX_NOPERM = 77
     """Permission denied."""
 
+    SEEDS_DO_NOT_MATCH = 125
+    """Provided seed and mnemonic phrase (seed generated from mnemonic phrase) do not match."""
+
     KEYBOARD_INTERRUPT = 130
     """Program received SIGINT."""
 
@@ -69,23 +79,119 @@ def cli_entry_point():
 
 
 def main(argv) -> ExitCode:
+    # TODO check for errors related to file IO
     logging.captureWarnings(True)
     warnings.simplefilter('always', ResourceWarning)
 
     config = Config.parse_args(argv[1:])  # argv[0] is program name
+    # On error with parsing argument, program was terminated by `Config.parse_args` with exit code 2 corresponding to
+    # `ExitCode.ARGUMENTS`. If arguments contained -h/--help or -V/--version, program was terminated wtih exit code 0,
+    # which corresponds to `ExitCode.E_OK`
     if config.logging_level:
-        coloredlogs.install(level=config.logging_level)
+        logging.basicConfig(format='%(asctime)s %(name)s[%(process)d] %(levelname)s %(message)s',
+                            level=config.logging_level)
     else:
         logging.disable(logging.CRITICAL)
     logger.debug('Config parsed from args.')
 
-    do_some_work(1)
+    read_mode = 'rb' if config.format is Config.Format.BINARY else 'r'
+    write_mode = 'wb' if config.format is Config.Format.BINARY else 'w'
+    # region # TODO What types of errors/exceptions can happen here?
+    if config.generate:
+        # TODO Check file size before reading?
+        with open(config.entropy_filepath, read_mode) as file:
+            entropy = file.read()  # type: typing.Union[bytes, str]
+        if config.format is Config.Format.TEXT_HEXADECIMAL:
+            entropy = unhexlify(entropy)  # type: bytes
+        if not is_valid_entropy(entropy):
+            msg = 'invalid entropy'
+            logger.critical(msg)
+            print(msg, file=sys.stderr)
+            return ExitCode.EX_DATAERR
+            # TODO We could use EAFP instead of LBYL here, as `generate` Raises: ValueError – on invalid parameters
+            # or we could use class for entropy, mnemonic, and seed which would validate inputs on instantiation and
+            # raise exceptions.
+        mnemonic, seed = generate(entropy, config.password)
+        with open(config.mnemonic_filepath, 'w') as file:
+            file.write(mnemonic)
+        logger.info('Mnemonic written to {}.'.format(config.mnemonic_filepath))
+        with open(config.seed_filepath, write_mode) as file:
+            if config.format is Config.Format.TEXT_HEXADECIMAL:
+                seed = hexlify(seed)
+            file.write(seed)
+        logger.info('Seed written to {}.'.format(config.seed_filepath))
+        print('[DONE] Generate, mnemonic in {}, seed in {}.'.format(config.mnemonic_filepath, config.seed_filepath))
+    elif config.recover:
+        # TODO Check file size before reading?
+        with open(config.mnemonic_filepath, 'r') as file:
+            mnemonic = file.read()  # type: str
+        if not is_valid_mnemonic(mnemonic):
+            msg = 'invalid mnemonic'
+            logger.critical(msg)
+            print(msg, file=sys.stderr)
+            return ExitCode.EX_DATAERR
+            # TODO We could use EAFP instead of LBYL here, as `recover` Raises: ValueError – on invalid parameters
+            # or we could use class for entropy, mnemonic, and seed which would validate inputs on instantiation and
+            # raise exceptions.
+        entropy, seed = recover(mnemonic, config.password)
+        with open(config.entropy_filepath, write_mode) as file:
+            if config.format is Config.Format.TEXT_HEXADECIMAL:
+                entropy = hexlify(entropy)
+            file.write(entropy)
+        logger.info('Entropy written to {}.'.format(config.entropy_filepath))
+        with open(config.seed_filepath, write_mode) as file:
+            if config.format is Config.Format.TEXT_HEXADECIMAL:
+                seed = hexlify(seed)
+            file.write(seed)
+        logger.info('Seed written to {}.'.format(config.seed_filepath))
+        print('[DONE] Recover, entropy in {}, seed in {}.'.format(config.entropy_filepath, config.seed_filepath))
+    elif config.verify:
+        # TODO Check file size before reading?
+        with open(config.mnemonic_filepath, 'r') as file:
+            mnemonic = file.read()  # type: str
+        if not is_valid_mnemonic(mnemonic):
+            msg = 'invalid mnemonic'
+            logger.critical(msg)
+            print(msg, file=sys.stderr)
+            return ExitCode.EX_DATAERR
+            # TODO We could use EAFP instead of LBYL here, as `recover` Raises: ValueError – on invalid parameters
+            # or we could use class for entropy, mnemonic, and seed which would validate inputs on instantiation and
+            # raise exceptions.
+        # TODO Check file size before reading?
+        with open(config.seed_filepath, read_mode) as file:
+            seed = file.read()  # type: typing.Union[bytes, str]
+        if config.format is Config.Format.TEXT_HEXADECIMAL:
+            seed = unhexlify(seed)  # type: bytes
+        if not is_valid_seed(seed):
+            msg = 'invalid seed'
+            logger.critical(msg)
+            print(msg, file=sys.stderr)
+            return ExitCode.EX_DATAERR
+            # TODO We could use EAFP instead of LBYL here, as `verify` Raises: ValueError – on invalid parameters
+            # or we could use class for entropy, mnemonic, and seed which would validate inputs on instantiation and
+            # raise exceptions.
+        match = verify(mnemonic, seed, config.password)
+        if not match:
+            msg = 'Seeds do not match.'
+            logger.info(msg)
+            print(msg, file=sys.stderr)
+            return ExitCode.SEEDS_DO_NOT_MATCH
+        msg = 'Seeds match.'
+        logger.info(msg)
+        print(msg)
+    # endregion
 
     logger.debug('exit code: {} {}'.format(ExitCode.EX_OK.name, ExitCode.EX_OK.value))
     return ExitCode.EX_OK
 
 
 class Config(object):
+    @unique
+    class Format(Enum):
+        """Formats for reading and writing entropy, seed, and mnemonic phrase."""
+        BINARY = 'bin'
+        TEXT_HEXADECIMAL = 'hex'
+
     PROGRAM_NAME = 'mnemoniccli'
     PROGRAM_DESCRIPTION = 'BIP39 Mnemonic Phrase Generator and Verifier'
     LOGGING_LEVELS_DICT = {'debug': logging.DEBUG,
@@ -97,8 +203,26 @@ class Config(object):
                            }
     LOGGING_LEVEL_DEFAULT = 'disabled'
 
-    def __init__(self):
-        self.logging_level = self.LOGGING_LEVELS_DICT[self.LOGGING_LEVEL_DEFAULT]  # type: int
+    def __init__(self,
+                 logging_level: int,
+                 entropy_filepath: str,
+                 seed_filepath: str,
+                 mnemonic_filepath: str,
+                 format_: Format,
+                 password: str,
+                 generate_: bool,
+                 recover_: bool,
+                 verify_: bool,
+                 ):
+        self.logging_level = logging_level
+        self.entropy_filepath = entropy_filepath
+        self.seed_filepath = seed_filepath
+        self.mnemonic_filepath = mnemonic_filepath
+        self.format = format_
+        self.password = password
+        self.generate = generate_
+        self.recover = recover_
+        self.verify = verify_
 
     @classmethod
     def init_parser(cls) -> argparse.ArgumentParser:
@@ -115,7 +239,7 @@ class Config(object):
                    'Faculty of Informatics (FI)\n'
                    'Masaryk University (MU)'
         )
-        parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(__version__))
+        parser.add_argument('-V', '--version', action='version', version='%(prog)s {}'.format(__version__))
         parser.add_argument('-ll', '--logging-level',
                             # NOTE: The type is called before check against choices. In order to display logging level
                             # names as choices, name to level int value conversion cannot be done here. Conversion is
@@ -124,12 +248,50 @@ class Config(object):
                             choices=cls.LOGGING_LEVELS_DICT,
                             help='select logging level (default: %(default)s)'
                             )
+        parser.add_argument('-e', '--entropy',
+                            help='path to file with entropy, input/output depends on action,',
+                            metavar='FILE'
+                            )
+        parser.add_argument('-s', '--seed',
+                            help='path to file with seed, input/output depends on action,',
+                            metavar='FILE'
+                            )
+        parser.add_argument('-m', '--mnemonic',
+                            help='path to file with mnemonic, input/output depends on action,',
+                            metavar='FILE'
+                            )
+        parser.add_argument('-f', '--format',
+                            default=cls.Format.TEXT_HEXADECIMAL.value,
+                            choices={f.value: f for f in cls.Format},
+                            help='select input and output format (default: %(default)s)'
+                            )
+        parser.add_argument('-p', '--password',
+                            help='password for protection of seed',
+                            default=''
+                            )
+        group = parser.add_mutually_exclusive_group(required=True)
+        group.add_argument('-g', '--generate',
+                           action='store_true',
+                           help='generate seed and mnemonic phrase from entropy'
+                           )
+        group.add_argument('-r', '--recover',
+                           action='store_true',
+                           help='recover entropy and seed from mnemonic phrase'
+                           )
+        group.add_argument('-v', '--verify',
+                           action='store_true',
+                           help='verify if provided phrase generates expected seed'
+                           )
         return parser
 
     @classmethod
     def parse_args(cls, args: Sequence[str]):
-        """
-        Parse command line arguments and store checked and converted values in Config object.
+        """Parse command line arguments and store checked and converted values in Config object.
+
+        According to default behaviour of `argparse.ArgumentParser`, this method terminates
+        program with exit code 2 (corresponding to `ExitCode.ARGUMENTS`) if passed arguments are
+        invalid. If arguments contain -h/--help or -V/--version, program is terminated with exit
+        code 0 (corresponding to `ExitCode.E_OK`).
         :type args: Sequence[str]
         :param args: argument strings
         """
@@ -137,9 +299,43 @@ class Config(object):
         # NOTE: Call to parse_args with namespace=self does not set logging_level with default value, if argument is not
         # in provided args.
         parsed_args = parser.parse_args(args=args)
-        config = cls()
-        # name to value conversion as noted in `self.init_parser`
-        config.logging_level = config.LOGGING_LEVELS_DICT[parsed_args.logging_level]
+        # basic input file path check
+        if parsed_args.generate:
+            if not parsed_args.entropy:
+                parser.error('argument entropy is required with action `generate`'.format(parsed_args.entropy))
+            if not os.path.isfile(parsed_args.entropy):
+                # TODO do not check `isfile` here, try to open it later and if it fails, termiante with EX_NOINPUT
+                parser.error('argument entropy: input file `{}` does not exist'.format(parsed_args.entropy))
+        elif parsed_args.recover:
+            if not parsed_args.mnemonic:
+                parser.error('argument mnemonic is required with action `recover`'.format(parsed_args.entropy))
+            if not os.path.isfile(parsed_args.mnemonic):
+                # TODO do not check `isfile` here, try to open it later and if it fails, termiante with EX_NOINPUT
+                parser.error('argument mnemonic: input file `{}` does not exist'.format(parsed_args.mnemonic))
+        elif parsed_args.verify:
+            if not parsed_args.mnemonic:
+                parser.error('argument mnemonic is required with action `verify`'.format(parsed_args.entropy))
+            if not parsed_args.seed:
+                parser.error('argument seed is required with action `verify`'.format(parsed_args.entropy))
+            if not os.path.isfile(parsed_args.mnemonic):
+                # TODO do not check `isfile` here, try to open it later and if it fails, termiante with EX_NOINPUT
+                parser.error('argument mnemonic: input file `{}` does not exist'.format(parsed_args.mnemonic))
+            if not os.path.isfile(parsed_args.seed):
+                # TODO do not check `isfile` here, try to open it later and if it fails, termiante with EX_NOINPUT
+                parser.error('argument seed: input file `{}` does not exist'.format(parsed_args.seed))
+
+        config = cls(
+            # name to value conversion as noted in `self.init_parser`
+            logging_level=cls.LOGGING_LEVELS_DICT[parsed_args.logging_level],
+            entropy_filepath=parsed_args.entropy,
+            mnemonic_filepath=parsed_args.mnemonic,
+            seed_filepath=parsed_args.seed,
+            format_=cls.Format(parsed_args.format),
+            password=parsed_args.password,
+            generate_=parsed_args.generate,
+            recover_=parsed_args.recover,
+            verify_=parsed_args.verify,
+        )
         return config
 
 
diff --git a/PA193_mnemonic_Slytherin/tests/__init__.py b/PA193_mnemonic_Slytherin/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/README.md b/README.md
index dbf56cb..ef9dbe0 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,5 @@
 # BIP39 Mnemonic Phrase Generator and Verifier
 
-[![Build Status](https://travis-ci.org/mvondracek/PA193_mnemonic_Slytherin.svg?branch=dev)](https://travis-ci.org/mvondracek/PA193_mnemonic_Slytherin)
-
 > [Secure Coding Principles and Practices (PA193)](https://is.muni.cz/course/fi/autumn2019/PA193?lang=en)<br/>
 > [Faculty of Informatics (FI)](https://www.fi.muni.cz/index.html.en)<br/>
 > [Masaryk University (MU)](https://www.muni.cz/en)
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 8bc5285..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-coloredlogs==10.0
-pytest==5.2.1
diff --git a/setup.py b/setup.py
index e75005c..7ea58eb 100644
--- a/setup.py
+++ b/setup.py
@@ -10,6 +10,7 @@ Team Slytherin: @sobuch, @lsolodkova, @mvondracek.
 
 2019
 """
+from os import path
 
 from setuptools import setup
 
@@ -17,7 +18,7 @@ __author__ = 'Team Slytherin: @sobuch, @lsolodkova, @mvondracek.'
 
 
 def readme():
-    with open('README.md') as f:
+    with open(path.join(path.dirname(path.abspath(__file__)), 'README.md')) as f:
         return f.read()
 
 
@@ -34,12 +35,8 @@ setup(
 
     python_requires='>=3.5, <4',
     packages=['PA193_mnemonic_Slytherin'],
-    install_requires=[
-        'coloredlogs',
-    ],
     tests_require=[
         'nose',
-        'pytest',
     ],
     test_suite='nose.collector',
     entry_points={

CLI
I'm working on CLI tool `mnemoniccli` and it should be finished on Monday evening.
diff --git a/PA193_mnemonic_Slytherin/tests/test_mnemonic.py b/PA193_mnemonic_Slytherin/test_mnemonic.py
similarity index 97%
rename from PA193_mnemonic_Slytherin/tests/test_mnemonic.py
rename to PA193_mnemonic_Slytherin/test_mnemonic.py
index a639750..23a3451 100644
--- a/PA193_mnemonic_Slytherin/tests/test_mnemonic.py
+++ b/PA193_mnemonic_Slytherin/test_mnemonic.py
@@ -1,3 +1,14 @@
+"""
+BIP39 Mnemonic Phrase Generator and Verifier
+
+Secure Coding Principles and Practices (PA193)  https://is.muni.cz/course/fi/autumn2019/PA193?lang=en
+Faculty of Informatics (FI)                     https://www.fi.muni.cz/index.html.en
+Masaryk University (MU)                         https://www.muni.cz/en
+
+Team Slytherin: @sobuch, @lsolodkova, @mvondracek.
+
+2019
+"""
 import unittest
 from binascii import unhexlify
 from unittest import TestCase
@@ -6,6 +17,7 @@ from PA193_mnemonic_Slytherin.mnemonic import do_some_work
 
 from PA193_mnemonic_Slytherin.mnemonic import generate, recover, verify
 
+
 class TestDoSomeWork(TestCase):
     """Placeholder for initial code structure."""
 
@@ -193,3 +205,7 @@ class TestMnemonicPublic(TestCase):
     def test_verify(self):
         for test_vector in TREZOR_TEST_VECTORS['english']:
             self.assertTrue(verify(test_vector[1], unhexlify(test_vector[2]), TREZOR_PASSWORD))
+
+
+if __name__ == '__main__':
+    unittest.main()
diff --git a/PA193_mnemonic_Slytherin/test_mnemoniccli.py b/PA193_mnemonic_Slytherin/test_mnemoniccli.py
new file mode 100644
index 0000000..dae1f43
--- /dev/null
+++ b/PA193_mnemonic_Slytherin/test_mnemoniccli.py
@@ -0,0 +1,97 @@
+"""
+BIP39 Mnemonic Phrase Generator and Verifier
+
+Secure Coding Principles and Practices (PA193)  https://is.muni.cz/course/fi/autumn2019/PA193?lang=en
+Faculty of Informatics (FI)                     https://www.fi.muni.cz/index.html.en
+Masaryk University (MU)                         https://www.muni.cz/en
+
+Team Slytherin: @sobuch, @lsolodkova, @mvondracek.
+
+2019
+"""
+import os
+import subprocess
+import unittest
+from tempfile import TemporaryDirectory
+
+from PA193_mnemonic_Slytherin.mnemoniccli import ExitCode
+
+
+class TestMain(unittest.TestCase):
+    """Integration tests for CLI tool."""
+    TIMEOUT = 5  # seconds until we terminate the program
+    PYTHON = 'python'
+    SCRIPT = 'mnemoniccli.py'
+    SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
+
+    def assert_argument_error(self, args):
+        cli = subprocess.run(args, timeout=self.TIMEOUT, cwd=self.SCRIPT_DIR,
+                             stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+        self.assertEqual('', cli.stdout)
+        self.assertNotEqual('', cli.stderr)
+        self.assertEqual(ExitCode.ARGUMENTS.value, cli.returncode)
+
+    def assert_argument_ok_terminated(self, args):
+        cli = subprocess.run(args, timeout=self.TIMEOUT, cwd=self.SCRIPT_DIR,
+                             stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+        self.assertNotEqual('', cli.stdout)
+        self.assertEqual('', cli.stderr)
+        self.assertEqual(ExitCode.EX_OK.value, cli.returncode)
+
+    def test_arguments_error(self):
+        """invalid arguments"""
+        self.assert_argument_error([self.PYTHON, self.SCRIPT])
+        self.assert_argument_error([self.PYTHON, self.SCRIPT, '-ll', 'FOO'])
+        self.assert_argument_error([self.PYTHON, self.SCRIPT, '-g'])
+        self.assert_argument_error([self.PYTHON, self.SCRIPT, '-r'])
+        self.assert_argument_error([self.PYTHON, self.SCRIPT, '-v'])
+        self.assert_argument_error([self.PYTHON, self.SCRIPT, '-g', '-r', '-v'])
+
+    def test_arguments_ok_terminated(self):
+        """correct argument resulting in termination"""
+        self.assert_argument_ok_terminated([self.PYTHON, self.SCRIPT, '-h'])
+        self.assert_argument_ok_terminated([self.PYTHON, self.SCRIPT, '--help'])
+        self.assert_argument_ok_terminated([self.PYTHON, self.SCRIPT, '-V'])
+        self.assert_argument_ok_terminated([self.PYTHON, self.SCRIPT, '--version'])
+
+    def test_arguments_error_file_path(self):
+        """input files don't exist"""
+        with TemporaryDirectory() as tmpdir:
+            non_existing_filepath = os.path.join(tmpdir, '__this_file_does_not_exist__')
+            self.assert_argument_error([self.PYTHON, self.SCRIPT, '-g', '-e', non_existing_filepath])
+            self.assert_argument_error([self.PYTHON, self.SCRIPT, '-r', '-m', non_existing_filepath])
+            self.assert_argument_error([self.PYTHON, self.SCRIPT, '-v', '-m', non_existing_filepath, '-s', non_existing_filepath])
+
+            with open(os.path.join(tmpdir, '__this_file_exists__.txt'), 'w') as f:
+                f.write('foo bar')
+            self.assert_argument_error([self.PYTHON, self.SCRIPT, '-v', '-m', f.name, '-s', non_existing_filepath])
+            self.assert_argument_error([self.PYTHON, self.SCRIPT, '-v', '-m', non_existing_filepath, '-s', f.name])
+
+    def test_invalid_entropy(self):
+        """Invalid input file with entropy
+        > The mnemonic must encode entropy in a multiple of 32 bits. With more entropy security is improved but
+        > the sentence length increases. We refer to the initial entropy length as ENT. The allowed size of ENT
+        > is 128-256 bits.
+        > https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki#generating-the-mnemonic
+        """
+        with TemporaryDirectory() as tmpdir:
+            # binary input file
+            # basic byte of entropy for this test
+            entropy_byte = b'\x01'
+            valid_entropy_bytes_lengths = list(range(16, 32 + 1, 4))
+            for entropy_bytes_length in range(0, 40):
+                if entropy_bytes_length in valid_entropy_bytes_lengths:
+                    continue
+                with self.subTest(entropy_bytes_length=entropy_bytes_length):
+                    with open(os.path.join(tmpdir, '__entropy_binary__.dat'), 'wb') as f:
+                        f.write(entropy_byte * entropy_bytes_length)
+                    cli = subprocess.run([self.PYTHON, self.SCRIPT, '-g', '-e', f.name, '--format', 'bin'],
+                                         timeout=self.TIMEOUT, cwd=self.SCRIPT_DIR,
+                                         stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
+                    self.assertEqual('', cli.stdout)
+                    self.assertEqual('invalid entropy\n', cli.stderr)
+                    self.assertEqual(ExitCode.EX_DATAERR.value, cli.returncode)
+
+
+if __name__ == '__main__':
+    unittest.main()

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest numpy>=1.16.0 pandas>=1.0.0
apt-get update apt-get install -y gcc
3.9
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
coloredlogs==15.0.1
exceptiongroup==1.2.2
humanfriendly==10.0
iniconfig==2.1.0
numpy==2.0.2
-e git+https://github.com/mvondracek/PA193_mnemonic_Slytherin.git@6f3c5ca4ba308233640655438e4d45f4b89549cc#egg=PA193_mnemonic_Slytherin
packaging==24.2
pandas==2.2.3
pluggy==1.5.0
pytest==8.3.5
python-dateutil==2.9.0.post0
pytz==2025.2
six==1.17.0
tomli==2.2.1
tzdata==2025.2

name: PA193_mnemonic_Slytherin
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - coloredlogs==15.0.1
      - exceptiongroup==1.2.2
      - humanfriendly==10.0
      - iniconfig==2.1.0
      - numpy==2.0.2
      - packaging==24.2
      - pandas==2.2.3
      - pluggy==1.5.0
      - pytest==8.3.5
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - six==1.17.0
      - tomli==2.2.1
      - tzdata==2025.2
prefix: /opt/conda/envs/PA193_mnemonic_Slytherin

PA193_mnemonic_Slytherin/test_mnemoniccli.py::TestMain::test_arguments_error PA193_mnemonic_Slytherin/test_mnemoniccli.py::TestMain::test_arguments_error_file_path PA193_mnemonic_Slytherin/test_mnemoniccli.py::TestMain::test_arguments_ok_terminated PA193_mnemonic_Slytherin/test_mnemoniccli.py::TestMain::test_invalid_entropy
PA193_mnemonic_Slytherin/test_mnemonic.py::TestDoSomeWork::test_do_some_work
setup.py PA193_mnemonic_Slytherin/mnemoniccli.py .travis.yml PA193_mnemonic_Slytherin/__init__.py README.md requirements.txt PA193_mnemonic_Slytherin/tests/__init__.py PA193_mnemonic_Slytherin/mnemonic.py
setup.py PA193_mnemonic_Slytherin/mnemoniccli.py .travis.yml PA193_mnemonic_Slytherin/__init__.py README.md requirements.txt PA193_mnemonic_Slytherin/tests/__init__.py PA193_mnemonic_Slytherin/mnemonic.py