subplot 0.14.0

tools for specifying, documenting, and implementing automated acceptance tests for systems and software
Documentation
from datetime import datetime

import logging
import os
import re
import shutil
import time


def files_relative(filename):
    return "./" + filename


def _files_create_from_embedded(
    ctx, filename_on_disk=None, embedded_file=None, executable=False
):
    get_file = globals()["get_file"]

    files_make_directory(ctx, path=os.path.dirname(filename_on_disk) or ".")
    filename_on_disk = files_relative(filename_on_disk)
    with open(filename_on_disk, "wb") as f:
        f.write(get_file(embedded_file))
    if executable:
        os.chmod(filename_on_disk, 0o755)


def files_create_from_embedded(ctx, embedded_file=None):
    _files_create_from_embedded(
        ctx,
        filename_on_disk=embedded_file,
        embedded_file=embedded_file,
        executable=False,
    )


def files_create_from_embedded_with_other_name(
    ctx, filename_on_disk=None, embedded_file=None
):
    _files_create_from_embedded(
        ctx,
        filename_on_disk=filename_on_disk,
        embedded_file=embedded_file,
        executable=False,
    )


def files_create_executable_from_embedded(ctx, embedded_file=None):
    _files_create_from_embedded(
        ctx,
        filename_on_disk=embedded_file,
        embedded_file=embedded_file,
        executable=True,
    )


def files_create_executable_from_embedded_with_other_name(
    ctx, filename_on_disk=None, embedded_file=None
):
    _files_create_from_embedded(
        ctx,
        filename_on_disk=filename_on_disk,
        embedded_file=embedded_file,
        executable=True,
    )


def files_create_from_text(ctx, filename=None, text=None):
    files_make_directory(ctx, path=os.path.dirname(filename) or ".")
    filename = files_relative(filename)
    with open(filename, "w") as f:
        f.write(text)


def files_remove_file(ctx, filename=None):
    filename = files_relative(filename)
    os.remove(filename)


def files_make_directory(ctx, path=None):
    path = files_relative(path)
    if not os.path.exists(path):
        os.makedirs(path)


def files_remove_directory(ctx, path=None):
    path = files_relative(path)
    shutil.rmtree(path)


def files_remove_empty_directory(ctx, path=None):
    path = files_relative(path)
    os.rmdir(path)


def files_file_exists(ctx, filename=None):
    assert_eq = globals()["assert_eq"]
    filename = files_relative(filename)
    assert_eq(os.path.exists(filename), True)


def files_file_does_not_exist(ctx, filename=None):
    assert_eq = globals()["assert_eq"]
    filename = files_relative(filename)
    assert_eq(os.path.exists(filename), False)


def files_directory_exists(ctx, path=None):
    assert_eq = globals()["assert_eq"]
    path = files_relative(path)
    assert_eq(os.path.isdir(path), True)


def files_directory_does_not_exist(ctx, path=None):
    assert_eq = globals()["assert_eq"]
    path = files_relative(path)
    assert_eq(os.path.isdir(path), False)


def files_directory_is_empty(ctx, path=None):
    assert_eq = globals()["assert_eq"]
    path = files_relative(path)
    assert_eq(os.listdir(path), [])


def files_directory_is_not_empty(ctx, path=None):
    assert_ne = globals()["assert_ne"]
    path = files_relative(path)
    assert_ne(os.listdir(path), False)


def files_only_these_exist(ctx, filenames=None):
    assert_eq = globals()["assert_eq"]
    filenames = filenames.replace(",", "").split()
    assert_eq(set(os.listdir(".")), set(filenames))


def files_file_contains(ctx, filename=None, data=None):
    assert_eq = globals()["assert_eq"]
    filename = files_relative(filename)
    with open(filename, "rb") as f:
        actual = f.read()
        actual = actual.decode("UTF-8")
    assert_eq(data in actual, True)


def files_file_doesnt_contain(ctx, filename=None, data=None):
    assert_eq = globals()["assert_eq"]
    filename = files_relative(filename)
    with open(filename, "rb") as f:
        actual = f.read()
        actual = actual.decode("UTF-8")
    assert_eq(data in actual, False)


def files_file_matches_regex(ctx, filename=None, regex=None):
    assert_eq = globals()["assert_eq"]
    filename = files_relative(filename)
    with open(filename) as f:
        content = f.read()
    m = re.search(regex, content)
    if m is None:
        logging.debug("files_file_matches_regex: no match")
        logging.debug(f"  filenamed: {filename}")
        logging.debug(f"  regex: {regex}")
        logging.debug(f"  content: {content}")
        logging.debug(f"  match: {m}")
    assert_eq(bool(m), True)


def files_match(ctx, filename1=None, filename2=None):
    assert_eq = globals()["assert_eq"]
    filename1 = files_relative(filename1)
    filename2 = files_relative(filename2)
    with open(filename1, "rb") as f:
        data1 = f.read()
    with open(filename2, "rb") as f:
        data2 = f.read()
    assert_eq(data1, data2)


def files_do_not_match(ctx, filename1=None, filename2=None):
    assert_ne = globals()["assert_ne"]
    filename1 = files_relative(filename1)
    filename2 = files_relative(filename2)
    with open(filename1, "rb") as f:
        data1 = f.read()
    with open(filename2, "rb") as f:
        data2 = f.read()
    assert_ne(data1, data2)


def file_and_embedded_file_match(ctx, filename=None, embedded=None):
    assert_eq = globals()["assert_eq"]
    get_file = globals()["get_file"]
    filename = files_relative(filename)
    with open(filename, "rb") as f:
        data1 = f.read()
    data2 = get_file(embedded)
    assert_eq(data1, data2)


def file_and_embedded_file_do_not_match(ctx, filename=None, embedded=None):
    assert_ne = globals()["assert_ne"]
    get_file = globals()["get_file"]
    filename = files_relative(filename)
    with open(filename, "rb") as f:
        data1 = f.read()
    data2 = get_file(embedded)
    assert_ne(data1, data2)


def files_touch_with_timestamp(ctx, filename=None, mtime=None):
    t = datetime.strptime(mtime + " +0000", "%Y-%m-%d %H:%M:%S %z")
    ts = t.timestamp()
    _files_touch(filename, ts)


def files_touch(ctx, filename=None):
    _files_touch(filename, None)


def _files_touch(filename, ts):
    filename = files_relative(filename)
    if not os.path.exists(filename):
        open(filename, "w").close()
    times = None
    if ts is not None:
        times = (ts, ts)
    os.utime(filename, times=times)


def files_mtime_is_recent(ctx, filename=None):
    filename = files_relative(filename)
    st = os.stat(filename)
    age = abs(st.st_mtime - time.time())
    assert age < 1.0


def files_mtime_is_ancient(ctx, filename=None):
    filename = files_relative(filename)
    st = os.stat(filename)
    age = abs(st.st_mtime - time.time())
    year = 365 * 24 * 60 * 60
    required = 39 * year
    logging.debug(f"ancient? mtime={st.st_mtime} age={age} required={required}")
    assert age > required


def files_remember_metadata(ctx, filename=None):
    log_value = globals()["log_value"]

    meta = _files_remembered(ctx)
    meta[filename] = _files_get_metadata(filename)
    logging.debug("files_remember_metadata:")
    log_value("meta", 1, meta)
    log_value("ctx", 1, ctx.as_dict())


# Check that current metadata of a file is as stored in the context.
def files_has_remembered_metadata(ctx, filename=None):
    assert_dict_eq = globals()["assert_dict_eq"]
    log_value = globals()["log_value"]

    meta = _files_remembered(ctx)
    logging.debug("files_has_remembered_metadata:")
    log_value("meta", 1, meta)
    log_value("ctx", 1, ctx.as_dict())

    assert_dict_eq(meta[filename], _files_get_metadata(filename))


def files_has_different_metadata(ctx, filename=None):
    assert_ne = globals()["assert_ne"]
    meta = _files_remembered(ctx)
    assert_ne(meta[filename], _files_get_metadata(filename))


def _files_remembered(ctx):
    ns = ctx.declare("_files")
    return ns.get("remembered-metadata", {})


def _files_get_metadata(filename):
    filename = files_relative(filename)
    st = os.lstat(filename)
    keys = ["st_dev", "st_gid", "st_ino", "st_mode", "st_mtime", "st_size", "st_uid"]
    return {key: getattr(st, key) for key in keys}