# HG changeset patch # User Pierre-Yves David <pierre-yves.david@octobus.net> # Date 1644226694 -3600 # Mon Feb 07 10:38:14 2022 +0100 # Node ID fd546c6ebd9c1ca773b9d83d52a9f43b30b190c0 # Parent 6f4d65d08ac0636898dbb9e5525e60ab37dc1d7f First prototype of a full chain We get an initial implementation for the following key part: - bin-env definition generation and usage - data-env usage - benchmark definition and usage - result comparison It is all very basic so it will need obvious improvement and stabilisation, but that should be solid base to start in the right direction. It is worth nothing that there is currently no glue between each steps. diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -2,4 +2,3 @@ ^tmp/ ^cache/ ^datasets/new.toml - diff --git a/bin/bin-env-util b/bin/bin-env-util new file mode 100755 --- /dev/null +++ b/bin/bin-env-util @@ -0,0 +1,318 @@ +#!/bin/env python3 +# +# Small tool to manipulaote +import argparse +import os +import subprocess +import sys +import tempfile +import toml + +ERR_CODE_NO_KEY = 1 +ERR_CODE_NO_BIN_ENV_SHELL = 2 +ERR_CODE_NO_BIN_ENV_DESC = 3 +ERR_CODE_EXISTS = 4 +ERR_CODE_NO_SETUP_SCRIPT = 6 +ERR_CODE_SETUP_SCRIPT_FAILED = 7 + +BIN_ENV_FILE = 'bin-env.poulpe' +SHELL_FILE = 'bin-env.shell' + + +def _bin_env_file(env_path): + """given the path of env, return the file of description file""" + return os.path.join(env_path, BIN_ENV_FILE) + + +def create_empty(env_path, method="manual"): + """create a new environ (in a non-ready state)""" + os.makedirs(env_path, exist_ok=True) + base_data = { + 'poulpe-environment': { + 'format-version': 0, + 'environment-type': 'binary', + 'setup-method': method, + }, + 'ready': False, + } + file_path = _bin_env_file(env_path) + if os.path.exists(file_path): + err('This is already a bin-env, aborting') + return ERR_CODE_EXISTS + _write_data(file_path, base_data) + return 0 + + +def setup_one(env_path, script): + if not os.access(script, os.X_OK): + err(f'no executable setup scrip at: "{script}"') + return ERR_CODE_NO_SETUP_SCRIPT + create_empty(env_path, method="script") + script = os.path.abspath(script) + try: + subprocess.check_call(script, cwd=env_path) + except subprocess.CalledProcessError as exc: + err(f'script returned with status {exc.returncode}: {script}') + return ERR_CODE_SETUP_SCRIPT_FAILED + + env_file = _bin_env_file(env_path) + data = _get_data(env_file) + vars_file = os.path.join(env_path, "POULPE-VARS") + if os.path.exists(vars_file): + with open(vars_file) as f: + for line in f: + k, v = line.split('=', 1) + k = k.strip() + v = v.strip() + k = f'bin-env-vars.{k}' + _set_one_value(data, k, v) + _write_data(env_file, data) + return mark_ready(env_path) + + +def mark_ready(env_path): + shell_path = os.path.join(env_path, SHELL_FILE) + if not os.access(shell_path, os.X_OK): + err(f'cannot find an executable file at: "{shell_path}"') + return ERR_CODE_NO_BIN_ENV_SHELL + + path = _bin_env_file(env_path) + data = _get_data(path) + if data is None: + err(f'missing file: "{path}"') + return ERR_CODE_NO_BIN_ENV_DESC + data['ready'] = True + _write_data(path, data) + return 0 + + +def _get_data(path): + try: + with open(path) as f: + return toml.load(f) + except FileNotFoundError: + return None + + +def _write_data(path, data): + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + toml.dump(data, f) + os.replace(f.name, path) + + +def _show_one_level(data, indent=''): + for k, v in sorted(data.items()): + if not isinstance(v, dict): + print(f"{indent}{k} = {v}") + else: + print(f"{indent}{k}:") + _show_one_level(v, indent + ' ') + + +def show(env_path): + path = _bin_env_file(env_path) + data = _get_data(path) + if data is None: + err(f'missing file: "{path}"') + return ERR_CODE_NO_BIN_ENV_DESC + _show_one_level(data) + return 0 + + +def get_value(path, key): + data = _get_data(path) + if data is None: + err(f'missing file: "{path}"') + return ERR_CODE_NO_BIN_ENV_DESC + + key_path = key.split('.') + sub = data + for k in key_path: + sub = sub.get(k) + if sub is None: + return ERR_CODE_NO_KEY + return 0 + + +def _set_one_value(data, key, value): + key_path = key.split('.') + sub = data + for k in key_path[:-1]: + val = sub.setdefault(k, {}) + sub = val + sub[key_path[-1]] = value + + +def set_value(path, key, value): + data = _get_data(path) + if data is None: + err(f'creating new file: "{path}"') + data = {} + + _set_one_value(data, key, value) + + _write_data(path, data) + return 0 + + +def del_value(path, key): + data = _get_data(path) + if data is None: + err(f'creating new file: "{path}"') + data = {} + + key_path = key.split('.') + sub = data + for k in key_path[:-1]: + sub = sub.setdefault(k, {}) + sub.pop(key_path[-1], None) + + _write_data(path, data) + return 0 + + +def err(*args, **kwargs): + """print something on stderr""" + print(*args, **kwargs, file=sys.stderr) + + +def _parsers(): + top_parser = argparse.ArgumentParser(prog='poulpe-bin-env-util') + subparsers = top_parser.add_subparsers( + help='available sub-command', + dest='command', + required=True + ) + + # XXX having PATH everywhere is not great + + ### bin-env-util create-empty PATH + cmd_parser = subparsers.add_parser( + 'create-empty', + help='create a new empty bin-env' + ) + cmd_parser.add_argument( + 'PATH', + help="the path to the (future) environment", + ) + + ### bin-env-util setup-one-sh PATH SCRIPT + cmd_parser = subparsers.add_parser( + 'setup-one', + help='create a new empty bin-env' + ) + cmd_parser.add_argument( + 'PATH', + help="the path of the environment", + ) + cmd_parser.add_argument( + 'SCRIPT', + help="the path to the script to setup the env", + ) + + ### bin-env-util mark-ready PATH + cmd_parser = subparsers.add_parser( + 'mark-ready', + help='mark a bin-env as ready' + ) + cmd_parser.add_argument( + 'PATH', + help="the path to the environment", + ) + + ### bin-env-util show PATH + cmd_parser = subparsers.add_parser( + 'show', + help='Show all data we have about this environment' + ) + cmd_parser.add_argument( + 'PATH', + help="the path to the environment", + ) + + ### bin-env-util get PATH KEY + cmd_parser = subparsers.add_parser( + 'get', + help='Show a specific bin-env variable' + ) + cmd_parser.add_argument( + 'PATH', + help="the path to the environment", + ) + cmd_parser.add_argument( + 'KEY', + help="the path to the variable", + ) + + ### bin-env-util set PATH KEY VALUE + cmd_parser = subparsers.add_parser( + 'set', + help='Show a specific bin-env variable' + ) + cmd_parser.add_argument( + 'PATH', + help="the path to the environment", + ) + cmd_parser.add_argument( + 'KEY', + help="the path to the variable", + ) + cmd_parser.add_argument( + 'VALUE', + help="the value to set", + ) + + ### bin-env-util del PATH KEY + cmd_parser = subparsers.add_parser( + 'del', + help='Show a specific bin-env variable' + ) + cmd_parser.add_argument( + 'PATH', + help="the path to the environment", + ) + cmd_parser.add_argument( + 'KEY', + help="the path to the variable", + ) + + return top_parser + + +def main(args): + parser = _parsers() + param = parser.parse_args(args) + if param.command == 'create-empty': + ret = create_empty(param.PATH) + elif param.command == 'setup-one': + ret = setup_one(param.PATH, param.SCRIPT) + elif param.command == 'mark-ready': + ret = mark_ready(param.PATH) + elif param.command == 'show': + ret = show(param.PATH) + elif param.command == 'get': + ret = get_value( + _bin_env_file(param.PATH), + f'bin-env-vars.{param.KEY}', + ) + elif param.command == 'set': + ret = set_value( + _bin_env_file(param.PATH), + f'bin-env-vars.{param.KEY}', + param.VALUE, + ) + elif param.command == 'del': + ret = del_value( + _bin_env_file(param.PATH), + f'bin-env-vars.{param.KEY}', + ) + else: + assert False + return ret + print(param) + + +if __name__ == "__main__": + ret = main(sys.argv[1:]) + assert ret is not None + sys.exit(ret) diff --git a/bin/diff-result b/bin/diff-result new file mode 100755 --- /dev/null +++ b/bin/diff-result @@ -0,0 +1,127 @@ +#!/bin/env python3 +# +# Small tool to manipulaote +import argparse +import json +import os +import stat +import subprocess +import sys +import tempfile +import toml + + +ERR_CODE_NO_KEY = 1 +ERR_CODE_NO_BIN_ENV_SHELL = 2 +ERR_CODE_NO_BIN_ENV_DESC = 3 +ERR_CODE_EXISTS = 4 +ERR_CODE_NO_SETUP_SCRIPT = 6 +ERR_CODE_SETUP_SCRIPT_FAILED = 7 + +BIN_ENV_FILE = 'bin-env.poulpe' +DATA_ENV_FILE = 'data-env.poulpe' +SHELL_FILE = 'bin-env.shell' + + +def _bin_env_file(bin_env_path): + """given the path of env, return the file of description file""" + return os.path.join(bin_env_path, BIN_ENV_FILE) + +def _bin_env_script(bin_env_path): + """given the path of env, return the file of description file""" + return os.path.join(bin_env_path, SHELL_FILE) + + +def _data_env_file(data_env_path): + """given the path of env, return the file of description file""" + return os.path.join(data_env_path, DATA_ENV_FILE) + + +def _get_data(path): + try: + with open(path) as f: + return toml.load(f) + except FileNotFoundError: + return None + +def _write_data(path, data): + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + toml.dump(data, f) + os.replace(f.name, path) + +def _get_one_value(data, key): + key_path = key.split('.') + sub = data + for k in key_path: + sub = sub.get(k) + if sub is None: + break + return sub + +def _set_one_value(data, key, value): + key_path = key.split('.') + sub = data + for k in key_path[:-1]: + val = sub.setdefault(k, {}) + sub = val + sub[key_path[-1]] = value + + +def set_value(path, key, value): + data = _get_data(path) + if data is None: + err(f'creating new file: "{path}"') + data = {} + + _set_one_value(data, key, value) + + _write_data(path, data) + return 0 + + +def err(*args, **kwargs): + """print something on stderr""" + print(*args, **kwargs, file=sys.stderr) + + +def compare(old_path, new_path): + old = _get_data(old_path) + assert old is not None + new = _get_data(new_path) + assert new is not None + + old_median = old['result']['time']['median'] + new_median = new['result']['time']['median'] + + diff = new_median - old_median + ratio = new_median / old_median + + print(f"{old_median:.4f} -> {new_median:.4f}: {diff:.4f} ({ratio:.2f})") + + return 0 + + +def _parsers(): + cmd_parser = argparse.ArgumentParser(prog='poulpe-bin-env-util') + + cmd_parser.add_argument( + 'OLD_RESULT', + help="the path to the binary environment directory", + ) + cmd_parser.add_argument( + 'NEW_RESULT', + help="the path to the data environment directory", + ) + return cmd_parser + + +def main(args): + parser = _parsers() + param = parser.parse_args(args) + ret = compare(param.OLD_RESULT, param.NEW_RESULT) + return ret + +if __name__ == "__main__": + ret = main(sys.argv[1:]) + assert ret is not None + sys.exit(ret) diff --git a/bin/env-desc b/bin/env-desc new file mode 100755 --- /dev/null +++ b/bin/env-desc @@ -0,0 +1,147 @@ +#!/bin/env python3 +# +# Small tool to manipulaote +import os +import sys +import tempfile +import toml + +ACTION_SHOW = 'show' +ACTION_SET = 'set' +ACTION_GET = 'get' +ACTION_DEL = 'del' +ALL_ACTIONS = { + ACTION_SHOW, + ACTION_SET, + ACTION_GET, + ACTION_DEL, +} + + +def _get_data(path): + try: + with open(path) as f: + return toml.load(f) + except FileNotFoundError: + return None + +def _write_data(path, data): + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + toml.dump(data, f) + os.replace(f.name, path) + + +def _show_one_level(data, indent=''): + if not isinstance(data, dict): + print(f"{indent}{data}") + return + for k, v in sorted(data.items()): + if isinstance(v, dict): + print(f"{indent}{k}:") + _show_one_level(v, indent + ' ') + elif isinstance(v, list): + print(f"{indent}{k}:") + for i in v: + _show_one_level(i, indent + '- ') + else: + print(f"{indent}{k} = {v}") + + +def show(path): + data = _get_data(path) + if data is None: + err(f'missing file: "{path}"') + return 3 + _show_one_level(data) + + +def get_value(path, key): + data = _get_data(path) + if data is None: + err(f'missing file: "{path}"') + return 3 + + key_path = key.split('.') + sub = data + for k in key_path: + sub = sub.get(k) + if sub is None: + return 1 + print(sub) + return 0 + + +def set_value(path, key, value): + data = _get_data(path) + if data is None: + err(f'creating new file: "{path}"') + data = {} + + key_path = key.split('.') + sub = data + for k in key_path[:-1]: + sub = sub.setdefault(k, {}) + sub[key_path[-1]] = value + + _write_data(path, data) + return 0 + + +def del_value(path, key): + data = _get_data(path) + if data is None: + err(f'creating new file: "{path}"') + data = {} + + key_path = key.split('.') + sub = data + for k in key_path[:-1]: + sub = sub.setdefault(k, {}) + sub.pop(key_path[-1], None) + + _write_data(path, data) + return 0 + +def err(*args, **kwargs): + print(*args, **kwargs, file=sys.stderr) + + +if __name__ == "__main__": + if len(sys.argv) < 3: + t = os.path.split(sys.argv[0])[-1] + err(f"{t}: small utility to manipulate environment description") + err("") + err(f" $ {t} show PATH - Display description content") + err(f" $ {t} get PATH key - Display key value") + err(f" $ {t} set PATH key value - Set key value") + err(f" $ {t} del PATH key - Remove key") + sys.exit(2) + action = sys.argv[1] + path = sys.argv[2] + key = value = None + + if action not in ALL_ACTIONS: + err(f"unknown action: {action}") + err("(use one of: %s)" % ', '.join(sorted(ALL_ACTIONS))) + sys.exit(2) + + if action != ACTION_SHOW: + if len(sys.argv) < 4: + err("missing key") + sys.exit(2) + key = sys.argv[3] + if action == ACTION_SET: + if len(sys.argv) < 5: + err("missing value") + sys.exit(2) + value = sys.argv[4] + + if action == ACTION_SHOW: + ret = show(path) + elif action == ACTION_GET: + ret = get_value(path, key) + elif action == ACTION_SET: + ret = set_value(path, key, value) + elif action == ACTION_DEL: + ret = del_value(path, key) +sys.exit(ret) diff --git a/bin/run-util b/bin/run-util new file mode 100755 --- /dev/null +++ b/bin/run-util @@ -0,0 +1,189 @@ +#!/bin/env python3 +# +# Small tool to manipulaote +import argparse +import json +import os +import stat +import subprocess +import sys +import tempfile +import toml + + +ERR_CODE_NO_KEY = 1 +ERR_CODE_NO_BIN_ENV_SHELL = 2 +ERR_CODE_NO_BIN_ENV_DESC = 3 +ERR_CODE_EXISTS = 4 +ERR_CODE_NO_SETUP_SCRIPT = 6 +ERR_CODE_SETUP_SCRIPT_FAILED = 7 + +BIN_ENV_FILE = 'bin-env.poulpe' +DATA_ENV_FILE = 'data-env.poulpe' +SHELL_FILE = 'bin-env.shell' + + +def _bin_env_file(bin_env_path): + """given the path of env, return the file of description file""" + return os.path.join(bin_env_path, BIN_ENV_FILE) + +def _bin_env_script(bin_env_path): + """given the path of env, return the file of description file""" + return os.path.join(bin_env_path, SHELL_FILE) + + +def _data_env_file(data_env_path): + """given the path of env, return the file of description file""" + return os.path.join(data_env_path, DATA_ENV_FILE) + + +def _get_data(path): + try: + with open(path) as f: + return toml.load(f) + except FileNotFoundError: + return None + +def _write_data(path, data): + with tempfile.NamedTemporaryFile(mode='w', delete=False) as f: + toml.dump(data, f) + os.replace(f.name, path) + +def _get_one_value(data, key): + key_path = key.split('.') + sub = data + for k in key_path: + sub = sub.get(k) + if sub is None: + break + return sub + +def _set_one_value(data, key, value): + key_path = key.split('.') + sub = data + for k in key_path[:-1]: + val = sub.setdefault(k, {}) + sub = val + sub[key_path[-1]] = value + + +def set_value(path, key, value): + data = _get_data(path) + if data is None: + err(f'creating new file: "{path}"') + data = {} + + _set_one_value(data, key, value) + + _write_data(path, data) + return 0 + + +def err(*args, **kwargs): + """print something on stderr""" + print(*args, **kwargs, file=sys.stderr) + + +def run_one(bin_env_path, data_env_path, benchmark, result): + result_data = {} + + # gather info about the binary environment + bin_env_desc = _bin_env_file(bin_env_path) + bin_env_data = _get_data(bin_env_desc) + result_data['bin-env-vars'] = bin_env_data['bin-env-vars'] + + # gather info about the data environment + data_env_desc = _data_env_file(data_env_path) + data_env_data = _get_data(data_env_desc) + result_data['data-env-vars'] = data_env_data['data-env-vars'] + + benchmark_data = _get_data(benchmark) + assert benchmark_data is not None, benchmark + result_data['benchmark'] = {} + result_data['benchmark']['name'] = benchmark_data['meta']['name'] + + # building the benchmark scenarion, that will likely changes often and quickly + + cmd = benchmark_data['simple-command']['command'] + + variables_data = benchmark_data['simple-command']['variables'] + + variables = {} + for name, value in variables_data.items(): + if value.startswith('DATA-VARS:'): + key = value.split(':', 1)[1] + value = _get_one_value(data_env_data, f'bench-input-vars.{key}') + assert value is not None + variables[name] = value + + cmd = cmd.format(**variables) + + r = _time_command(bin_env_path, data_env_path, cmd) + + # we should store more + result_data['result'] = {} + result_data['result']['time'] = {} + result_data['result']['time']['median'] = r['results'][0]['median'] + + _write_data(result, result_data) + return 0 + + +def _time_command(bin_env_path, data_env_path, cmd): + bin_env_path = os.path.abspath(bin_env_path) + data_env_path = os.path.abspath(data_env_path) + shell_path = _bin_env_script(bin_env_path) + + with tempfile.NamedTemporaryFile('w') as tmp_result: + time_cmd = [ + shell_path, + "hyperfine", + cmd, + "--export-json", + tmp_result.name, + "--ignore-failure", + ] + + subprocess.check_call( + time_cmd, + cwd=data_env_path, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + + with open(tmp_result.name) as f: + return json.load(f) + + +def _parsers(): + cmd_parser = argparse.ArgumentParser(prog='poulpe-bin-env-util') + + cmd_parser.add_argument( + 'BIN_ENV', + help="the path to the binary environment directory", + ) + cmd_parser.add_argument( + 'DATA_ENV', + help="the path to the data environment directory", + ) + cmd_parser.add_argument( + 'BENCHMARK', + help="the path to the benchmark description file", + ) + cmd_parser.add_argument( + 'RESULT', + help="the path to result file", + ) + return cmd_parser + + +def main(args): + parser = _parsers() + param = parser.parse_args(args) + ret = run_one(param.BIN_ENV, param.DATA_ENV, param.BENCHMARK, param.RESULT) + return ret + +if __name__ == "__main__": + ret = main(sys.argv[1:]) + assert ret is not None + sys.exit(ret) diff --git a/tests/test-bin-env-util.t b/tests/test-bin-env-util.t new file mode 100644 --- /dev/null +++ b/tests/test-bin-env-util.t @@ -0,0 +1,216 @@ +Test the utility that manage bin-env creation +--------------------------------------------- + + $ PATH="${TESTDIR}/../bin:$PATH" + +Check basic invocation + + $ bin-env-util --help + usage: poulpe-bin-env-util [-h] + {create-empty,setup-one,mark-ready,show,get,set,del} + ... + + positional arguments: + {create-empty,setup-one,mark-ready,show,get,set,del} + available sub-command + create-empty create a new empty bin-env + setup-one create a new empty bin-env + mark-ready mark a bin-env as ready + show Show all data we have about this environment + get Show a specific bin-env variable + set Show a specific bin-env variable + del Show a specific bin-env variable + + optional arguments: + -h, --help show this help message and exit + + +Check repository creation + +simple creation + + $ bin-env-util create-empty test-create + $ ls -1 test-create + bin-env.poulpe + $ bin-env-util show test-create + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = manual + ready = False + +Running on an existing repo abort and does not destroy it + + $ bin-env-util create-empty test-create + This is already a bin-env, aborting + [4] + $ bin-env-util show test-create + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = manual + ready = False + +Creating a repository on an existing dir + + $ mkdir test-existing-dir + $ bin-env-util create-empty test-existing-dir + $ bin-env-util show test-existing-dir + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = manual + ready = False + +Check manual setup +------------------ + +Check changing variable manually + + $ python3 -m venv test-existing-dir + $ cd test-existing-dir + $ bin/pip3 install --quiet black==18.6b4 + $ bin-env-util set . python.version "`bin/python --version | egrep -o '\S+$'`" + $ bin-env-util set . black.version "`bin/black --version | egrep -o '\S+$'`" + $ bin-env-util set . black.install-method pip + $ bin-env-util show . + bin-env-vars: + black: + install-method = pip + version = 18.6b4 + python: + version = 3.* (glob) + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = manual + ready = False + +Marking it ready when it is not + + $ bin-env-util mark-ready . + cannot find an executable file at: "./bin-env.shell" + [2] + $ bin-env-util show . + bin-env-vars: + black: + install-method = pip + version = 18.6b4 + python: + version = 3.* (glob) + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = manual + ready = False + +Manually create an activate script + + $ cat > bin-env.shell << EOF + > #!/bin/bash + > BIN_ENV_PATH="\`dirname \"\$0\"\`" + > . "\$BIN_ENV_PATH"/bin/activate + > "\$@" + > EOF + $ bin-env-util mark-ready . + cannot find an executable file at: "./bin-env.shell" + [2] + $ chmod +x bin-env.shell + +Marking it ready when it is + + $ bin-env-util mark-ready . + $ bin-env-util show . + bin-env-vars: + black: + install-method = pip + version = 18.6b4 + python: + version = 3.* (glob) + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = manual + ready = True + +Basic test of the result + + $ ./bin-env.shell echo babar + babar + $ ./bin-env.shell black --version + black, version 18.6b4 + $ cd .. + +Test scripted setup +------------------- + +Create setup scripts + + $ cat > setup-error.sh << EOF + > #!/bin/bash + > exit 2 + > EOF + $ chmod +x setup-error.sh + + $ grep -v 'chmod' $TESTDIR/test-data/setup-black.sh > setup-black-bad-activate.sh + $ chmod +x setup-black-bad-activate.sh + +Test a missing script + + $ bin-env-util setup-one env-missing setup-missing.sh + no executable setup scrip at: "setup-missing.sh" + [6] + +Test a setup error + + $ bin-env-util setup-one env-error setup-error.sh + script returned with status 2: $TESTTMP/setup-error.sh + [7] + +Test an invalid activate + + $ bin-env-util setup-one env-bad-activate setup-black-bad-activate.sh + $TESTTMP/setup-black-bad-activate.sh: line 5: BLACK_VERSION: unbound variable + script returned with status 1: $TESTTMP/setup-black-bad-activate.sh + [7] + $ rm -rf env-bad-activate + $ BLACK_VERSION="18.6b4" bin-env-util setup-one env-bad-activate setup-black-bad-activate.sh + cannot find an executable file at: "env-bad-activate/bin-env.shell" + [2] + +Test a succesfull script + + $ BLACK_VERSION="18.6b4" bin-env-util setup-one env-black-18.6b4 \ + > $TESTDIR/test-data/setup-black.sh + $ BLACK_VERSION="18.6b1" bin-env-util setup-one env-black-18.6b1 \ + > $TESTDIR/test-data/setup-black.sh + + $ bin-env-util show env-black-18.6b1 + bin-env-vars: + black: + install-method = pip + version = 18.6b1 + python: + version = 3.9.9 + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = script + ready = True + $ env-black-18.6b1/bin-env.shell black --version + black, version 18.6b1 + + $ bin-env-util show env-black-18.6b4 + bin-env-vars: + black: + install-method = pip + version = 18.6b4 + python: + version = 3.9.9 + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = script + ready = True + $ env-black-18.6b4/bin-env.shell black --version + black, version 18.6b4 diff --git a/tests/test-data/setup-black.sh b/tests/test-data/setup-black.sh new file mode 100755 --- /dev/null +++ b/tests/test-data/setup-black.sh @@ -0,0 +1,20 @@ +#!/bin/bash +set -euo pipefail + +python3 -m venv . +bin/pip3 install --quiet black=="$BLACK_VERSION" + +echo python.version="`bin/python --version | egrep -o '\S+$'`" > POULPE-VARS +echo black.version="`bin/black --version | egrep -o '\S+$'`" >> POULPE-VARS +echo black.install-method=pip >> POULPE-VARS + +touch bin-env.shell +chmod +x bin-env.shell + +cat >> bin-env.shell << EOF +#!/bin/bash +set -euo pipefail +BIN_ENV_PATH="\`dirname \"\$0\"\`" +. "\$BIN_ENV_PATH"/bin/activate +"\$@" +EOF diff --git a/tests/test-env-desc.t b/tests/test-env-desc.t new file mode 100644 --- /dev/null +++ b/tests/test-env-desc.t @@ -0,0 +1,108 @@ +Test the small utility that access environment description +---------------------------------------------------------- + + $ PATH="${TESTDIR}/../bin:$PATH" + +Check basic error +---------------- + +no args + + $ env-desc + env-desc: small utility to manipulate environment description + + $ env-desc show PATH - Display description content + $ env-desc get PATH key - Display key value + $ env-desc set PATH key value - Set key value + $ env-desc del PATH key - Remove key + [2] + +too few args + + $ env-desc foo + env-desc: small utility to manipulate environment description + + $ env-desc show PATH - Display description content + $ env-desc get PATH key - Display key value + $ env-desc set PATH key value - Set key value + $ env-desc del PATH key - Remove key + [2] + +bad action + + $ env-desc foo bar + unknown action: foo + (use one of: del, get, set, show) + [2] + +missing GET key + + $ env-desc get no-file + missing key + [2] + +missing SET key + + $ env-desc set no-file + missing key + [2] + +missing SET value + + $ env-desc set no-file key + missing value + [2] + +missing DEL key + + $ env-desc del no-file + missing key + [2] + +Missing file when reading + + $ env-desc show no-file + missing file: "no-file" + [3] + + $ env-desc get no-file no-key + missing file: "no-file" + [3] + +Writing and reading files +------------------------- + +Small and basic usage to smoke test the script + + $ env-desc set bin-env.pe vars.hg.version 6.1.0 + creating new file: "bin-env.pe" + $ env-desc set bin-env.pe vars.hg.flavor pure + $ env-desc set bin-env.pe vars.oops babar + $ env-desc get bin-env.pe vars.hg.version + 6.1.0 + $ env-desc get bin-env.pe vars.hg.flavor + pure + $ env-desc get bin-env.pe vars.oops + babar + $ env-desc show bin-env.pe + vars: + hg: + flavor = pure + version = 6.1.0 + oops = babar + +Modify the file more + + $ env-desc set bin-env.pe vars.hg.version 4.3.0 + $ env-desc del bin-env.pe vars.oops + $ env-desc get bin-env.pe vars.hg.version + 4.3.0 + $ env-desc get bin-env.pe vars.hg.flavor + pure + $ env-desc get bin-env.pe vars.oops + [1] + $ env-desc show bin-env.pe + vars: + hg: + flavor = pure + version = 4.3.0 diff --git a/tests/test-simple-run.t b/tests/test-simple-run.t new file mode 100644 --- /dev/null +++ b/tests/test-simple-run.t @@ -0,0 +1,130 @@ +Check we have enough simple pieces together to do a simple run +-------------------------------------------------------------- + + $ PATH="${TESTDIR}/../bin:$PATH" + +Setup the bin-env +----------------- + + $ BLACK_VERSION="18.6b4" bin-env-util setup-one bin-env-black-18.6b4 \ + > $TESTDIR/test-data/setup-black.sh + $ bin-env-util show bin-env-black-18.6b4 + bin-env-vars: + black: + install-method = pip + version = 18.6b4 + python: + version = 3.* (glob) + poulpe-environment: + environment-type = binary + format-version = 0 + setup-method = script + ready = True + + +Setup a data-env +---------------- + +(currently built by hand as it simple and mostly innert) + + $ mkdir data-env + $ env-desc set data-env/data-env.poulpe poulpe-environment.environment-type data + creating new file: "data-env/data-env.poulpe" + $ env-desc set data-env/data-env.poulpe poulpe-environment.format-version 0 + $ env-desc set data-env/data-env.poulpe poulpe-environment.setup-method manual + $ env-desc set data-env/data-env.poulpe data-env-vars.name black-bench + $ mkdir data-env/py-files + $ cat << EOF > data-env/py-files/good.py + > foo = [1, 2, 3, 4, 5] + > EOF + $ cat << EOF > data-env/py-files/bad.py + > foo = [1, + > 2, + > 3, + > 4, + > 5] + > EOF + $ env-desc set data-env/data-env.poulpe bench-input-vars.black.check.tiny.good py-files/good.py + $ env-desc set data-env/data-env.poulpe bench-input-vars.black.check.tiny.bad py-files/bad.py + +(that one will be a string, so its not good.) + + $ env-desc set data-env/data-env.poulpe ready 1 + + $ env-desc show data-env/data-env.poulpe + bench-input-vars: + black: + check: + tiny: + bad = py-files/bad.py + good = py-files/good.py + data-env-vars: + name = black-bench + poulpe-environment: + environment-type = data + format-version = 0 + setup-method = manual + ready = 1 + +Define a benchmark +------------------ + + $ env-desc set black-tiny-bad.pbd meta.format 0 + creating new file: "black-tiny-bad.pbd" + $ env-desc set black-tiny-bad.pbd meta.name black.check.tiny + $ env-desc set black-tiny-bad.pbd meta.method simple-command + $ env-desc set black-tiny-bad.pbd simple-command.command "black --check {file}" + $ env-desc set black-tiny-bad.pbd simple-command.variables.file DATA-VARS:black.check.tiny.bad + + $ env-desc show black-tiny-bad.pbd + meta: + format = 0 + method = simple-command + name = black.check.tiny + simple-command: + command = black --check {file} + variables: + file = DATA-VARS:black.check.tiny.bad + +Run the benchmark +----------------- + $ run-util bin-env-black-18.6b4 data-env black-tiny-bad.pbd test-result-18.6b4-bad.pbr + $ env-desc show test-result-18.6b4-bad.pbr + benchmark: + name = black.check.tiny + bin-env-vars: + black: + install-method = pip + version = 18.6b4 + python: + version = 3.* (glob) + data-env-vars: + name = black-bench + result: + time: + median = * (glob) + +Quick comparision of values +--------------------------- + +Get another result +(the sed if likely to not be a viable option in the future) + + $ cp black-tiny-bad.pbd black-tiny-good.pbd + $ sed -i 's/bad/good/g' black-tiny-good.pbd + $ env-desc show black-tiny-good.pbd + meta: + format = 0 + method = simple-command + name = black.check.tiny + simple-command: + command = black --check {file} + variables: + file = DATA-VARS:black.check.tiny.good + + $ run-util bin-env-black-18.6b4 data-env black-tiny-good.pbd test-result-18.6b4-good.pbr + +Compare the result + + $ diff-result test-result-18.6b4-bad.pbr test-result-18.6b4-good.pbr + \d+.\d+ -> \d+.\d+: -?\d+.\d+ \(\d+.\d+\) (re)