From 4b3b18b8d422bc9b66f65d87dacd0e880867f131 Mon Sep 17 00:00:00 2001 From: "Jason M. Gates" Date: Wed, 10 Jul 2024 08:50:00 -0600 Subject: [PATCH 1/6] ci: Switch from Mac to Ubuntu --- .github/workflows/continuous-integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 285df7b..6efaf32 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -13,7 +13,7 @@ defaults: jobs: test: - runs-on: macos-latest + runs-on: ubuntu-latest strategy: matrix: version: ["3.8", "3.9", "3.10", "3.11", "3.12"] From e2ae8034fc105c10a0caa03c0b67b1f3102c9a31 Mon Sep 17 00:00:00 2001 From: "Jason M. Gates" Date: Wed, 10 Jul 2024 08:51:52 -0600 Subject: [PATCH 2/6] test: Comment out all existing tests Also temporarily remove minimum coverage. --- .coveragerc | 2 +- test/test_shell_logger.py | 1830 ++++++++++++++++++------------------- 2 files changed, 916 insertions(+), 916 deletions(-) diff --git a/.coveragerc b/.coveragerc index d306237..8f8b416 100644 --- a/.coveragerc +++ b/.coveragerc @@ -11,7 +11,7 @@ source = shell_logger [report] skip_covered = False -fail_under = 80 +fail_under = 0 show_missing = True exclude_lines = pragma: no cover diff --git a/test/test_shell_logger.py b/test/test_shell_logger.py index 068ef78..cd4f600 100644 --- a/test/test_shell_logger.py +++ b/test/test_shell_logger.py @@ -25,918 +25,918 @@ psutil = None -@pytest.fixture(autouse=True) -def _use_tmpdir(monkeypatch: MonkeyPatch, tmpdir: Path) -> None: - """ - Use a temporary directory for all tests. - - Parameters: - monkeypatch: The ``MonkeyPatch`` fixture. - tmpdir: The temporary directory to use. - """ - monkeypatch.chdir(tmpdir) - - -@pytest.fixture() -def shell_logger() -> ShellLogger: - """ - Pre-populate a :class:`ShellLogger` for use in the tests. - - This fixture creates a :class:`ShellLogger` object with some sample - data to be used in tests. It first creates a sample - :class:`ShellLogger` object. Then it logs a command (whose - ``stdout`` is ``'Hello world'`` and ``stderr`` is ``'Hello world - error'``). Next, it adds a child :class:`ShellLogger` object and - prints something using that child logger. - - Returns: - The parent :class:`ShellLogger` object described above. - """ - # Initialize a parent `ShellLogger`. - parent = ShellLogger("Parent", log_dir=Path.cwd()) - - # Run the command. - # `stdout` ; `stderr` - cmd = ( - "sleep 1; echo 'Hello world out'; sleep 1; echo 'Hello world error' " - "1>&2" - ) - measure = ["cpu", "memory", "disk"] - kwargs = {"measure": measure, "return_info": True, "interval": 0.1} - if os.uname().sysname == "Linux": - kwargs.update( - {"trace": "ltrace", "expression": "setlocale", "summary": True} - ) - else: - print( - f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested." - ) - parent.log("test cmd", cmd, cwd=Path.cwd(), **kwargs) - parent.print("This is a message") - - # Add a child and run some commands. - child = parent.add_child("Child") - child.print("Hello world child") - child.log("Test out HTML characters", "echo ' &\"'\"'\"") - child.log("ls", "ls") - child.print("Hello again child") - - # Add more to the parent and return the object. - parent.print("This is another message") - return parent - - -def test_initialization_creates_stream_dir() -> None: - """ - Ensure the stream directory is created. - - Verify the initialization of a parent :class:`ShellLogger` object - creates a temporary directory - (``log_dir/%Y-%m-%d_%H%M%S``) if not already created. - """ - logger = ShellLogger(stack()[0][3], log_dir=Path.cwd()) - timestamp = logger.init_time.strftime("%Y-%m-%d_%H.%M.%S.%f") - assert len(list(Path.cwd().glob(f"{timestamp}_*"))) == 1 - - -def test_initialization_creates_html_file() -> None: - """ - Ensure the HTML file is created. - - Verify the initialization of a parent :class:`ShellLogger` object - creates a starting HTML file in the :attr:`log_dir`. - """ - logger = ShellLogger(stack()[0][3], log_dir=Path.cwd()) - timestamp = logger.init_time.strftime("%Y-%m-%d_%H.%M.%S.%f") - streamm_dir = next(Path.cwd().glob(f"{timestamp}_*")) - assert (streamm_dir / f"{stack()[0][3]}.html").exists() - - -def test_log_method_creates_tmp_stdout_stderr_files( - shell_logger: ShellLogger, -) -> None: - """ - Ensure output files are created. - - Verify that logging a command will create files in the - :class:`ShellLogger` object's :attr:`stream_dir` corresponding to - the ``stdout`` and ``stderr`` of the command. - - Parameters: - shell_logger: A pre-populated :class:`ShellLogger` object. - """ - # Get the paths for the `stdout`/`stderr` files. - cmd_id = shell_logger.log_book[0]["cmd_id"] - cmd_ts = shell_logger.log_book[0]["timestamp"] - stdout_file = shell_logger.stream_dir / f"{cmd_ts}_{cmd_id}_stdout" - stderr_file = shell_logger.stream_dir / f"{cmd_ts}_{cmd_id}_stderr" - assert stdout_file.exists() - assert stderr_file.exists() - print(f"{stdout_file}") - print(f"{stderr_file}") - - # Make sure the information written to these files is correct. - with stdout_file.open("r") as out, stderr_file.open("r") as err: - out_txt = out.readline() - err_txt = err.readline() - assert "Hello world out" in out_txt - assert "Hello world error" in err_txt - - -@pytest.mark.parametrize("return_info", [True, False]) -def test_log_method_return_info_works_correctly( - return_info: bool, # noqa: FBT001 -) -> None: - """ - Ensure ``stdout``/``stderr`` are returned when requested. - - Verify that when ``return_info=True``, we receive a dictionary that - contains the ``stdout`` and ``stderr`` of the command, as well as - the ``return_code``, and when ``return_info=False``, we receive the - ``return_code``, but ``stdout`` and ``stderr`` are ``None``. - - Parameters: - return_info: Whether or not to return the - ``stdout``/``stderr``. - """ - logger = ShellLogger(stack()[0][3], log_dir=Path.cwd()) - - # `stdout` ; `stderr` - cmd = "echo 'Hello world out'; echo 'Hello world error' 1>&2" - result = logger.log( - "test cmd", - cmd, - cwd=Path.cwd(), - return_info=return_info, - ) - if return_info: - assert "Hello world out" in result["stdout"] - assert "Hello world error" in result["stderr"] - assert result["return_code"] == 0 - else: - assert result["stdout"] is None - assert result["stderr"] is None - assert result["return_code"] == 0 - - -@pytest.mark.parametrize("live_stdout", [True, False]) -@pytest.mark.parametrize("live_stderr", [True, False]) -def test_log_method_live_stdout_stderr_works_correctly( - capsys: CaptureFixture, - live_stdout: bool, # noqa: FBT001 - live_stderr: bool, # noqa: FBT001 -) -> None: - """ - Ensure live streaming of ``stdout``/``stderr`` works. - - Verify that the ``live_stdout`` and ``live_stdout`` flags work as - expected for the :func:`log` method. - - Parameters: - capsys: A fixture to capture ``stdout``/``stderr``. - live_stdout: Whether or not to capture ``stdout`` while running - the :func:`log` command. - live_stderr: Whether or not to capture ``stderr`` while running - the :func:`log` command. - """ - logger = ShellLogger(stack()[0][3], log_dir=Path.cwd()) - cmd = "echo 'Hello world out'; echo 'Hello world error' 1>&2" - logger.log( - "test cmd", - cmd, - cwd=Path.cwd(), - live_stdout=live_stdout, - live_stderr=live_stderr, - ) - out, err = capsys.readouterr() - if live_stdout: - assert re.search(r"^Hello world out(\r)?\n", out) is not None - else: - assert re.search(r"^Hello world out(\r)?\n", out) is None - if live_stderr: - assert re.search(r"^Hello world error(\r)?\n", err) is not None - else: - assert re.search(r"^Hello world error(\r)?\n", err) is None - - -def test_child_logger_duration_displayed_correctly_in_html( - shell_logger: ShellLogger, -) -> None: - """ - Ensure child logger durations displays correctly. - - Verify that the overview of child loggers in the HTML file displays - the correct child :class:`ShellLogger` duration, not the entire - log's duration. - - Parameters: - shell_logger: A pre-populated :class:`ShellLogger` object. - """ - child2 = shell_logger.add_child("Child 2") - child2.log("Wait 0.005s", "sleep 0.005") - child3 = shell_logger.add_child("Child 3") - child3.log("Wait 0.006s", "sleep 0.006") - shell_logger.finalize() - with shell_logger.html_file.open("r") as hf: - html_text = hf.read() - assert child2.duration is not None - assert f"Duration: {child2.duration}" in html_text - assert child3.duration is not None - assert f"Duration: {child3.duration}" in html_text - - -def test_finalize_creates_json_with_correct_information( - shell_logger: ShellLogger, -) -> None: - """ - Ensure :func:`finalize` creates a JSON file with the proper data. - - Parameters: - shell_logger: A pre-populated :class:`ShellLogger` object. - """ - shell_logger.finalize() - - # Load from JSON. - json_file = shell_logger.stream_dir / "Parent.json" - assert json_file.exists() - with json_file.open("r") as jf: - loaded_logger = json.load(jf, cls=ShellLoggerDecoder) - - # Parent `ShellLogger`. - assert shell_logger.log_dir == loaded_logger.log_dir - assert shell_logger.stream_dir == loaded_logger.stream_dir - assert shell_logger.html_file == loaded_logger.html_file - assert shell_logger.indent == loaded_logger.indent - assert shell_logger.name == loaded_logger.name - assert shell_logger.init_time == loaded_logger.init_time - assert shell_logger.done_time == loaded_logger.done_time - assert shell_logger.log_book[0] == loaded_logger.log_book[0] - - # Child `ShellLogger`. - child = shell_logger.log_book[2] - loaded_child = loaded_logger.log_book[2] - assert child.log_dir == loaded_child.log_dir - assert child.stream_dir == loaded_child.stream_dir - assert child.html_file == loaded_child.html_file - assert child.indent == loaded_child.indent - assert child.name == loaded_child.name - assert child.init_time == loaded_child.init_time - assert child.done_time == loaded_child.done_time - assert child.log_book[0] == loaded_child.log_book[0] - - -def test_finalize_creates_html_with_correct_information( - shell_logger: ShellLogger, -) -> None: - """ - Ensure :func:`finalize` creates a HTML file with the proper data. - - Parameters: - shell_logger: A pre-populated :class:`ShellLogger` object. - """ - shell_logger.finalize() - - # Load the HTML file. - html_file = shell_logger.stream_dir / "Parent.html" - assert html_file.exists() - with html_file.open("r") as hf: - html_text = hf.read() - - # Ensure the command information is present. - assert ">test cmd {shell_logger.log_book[0]['timestamp']}" in html_text - assert ( - "Command:
sleep 1; echo 'Hello world out'; sleep 1; "
-        "echo 'Hello world error' 1>&2"
-    ) in html_text
-    assert f"CWD: {Path.cwd()}" in html_text
-    assert "Return Code: 0" in html_text
-
-    # Check to see that the print statement information is there.
-    assert "Hello world child" in html_text
-    if os.uname().sysname == "Linux":
-        assert "traceMemory Usage' in html_text
-    assert "" in html_text
-    assert 'class="card-title">CPU Usage' in html_text
-    assert 'class="card-title">Used Space on /' in html_text
-    assert "Environment" in html_text
-    assert "User:" in html_text
-    assert "Group:" in html_text
-    assert "Shell:" in html_text
-    assert "umask:" in html_text
-    assert "ulimit None:
-    """
-    Ensure :func:`finalize` creates the appropriate symlink.
-
-    Verify that the :func:`finalize` method symlinks
-    ``log_dir/html_file`` to ``streamm_dir/html_file``.
-
-    Parameters:
-        shell_logger:  A pre-populated :class:`ShellLogger` object.
-    """
-    shell_logger.finalize()
-
-    # Load the HTML file.
-    html_file = shell_logger.stream_dir / "Parent.html"
-    html_symlink = shell_logger.log_dir / "Parent.html"
-    assert html_file.exists()
-    assert html_symlink.exists()
-    assert html_symlink.resolve() == html_file
-
-
-def test_json_file_can_reproduce_html_file(
-    shell_logger: ShellLogger,
-) -> None:
-    """
-    Ensure the JSON file can regenerate the HTML.
-
-    Verify that a JSON file can properly recreate the original HTML file
-    created when :func:`finalize` is called.
-
-    Parameters:
-        shell_logger:  A pre-populated :class:`ShellLogger` object.
-    """
-    shell_logger.finalize()
-
-    # Load the original HTML file's contents.
-    html_file = shell_logger.log_dir / "Parent.html"
-    assert html_file.exists()
-    with html_file.open("r") as hf:
-        original_html = hf.read()
-
-    # Delete the HTML file.
-    html_file.unlink()
-
-    # Load the JSON data.
-    json_file = shell_logger.stream_dir / "Parent.json"
-    assert json_file.exists()
-    with json_file.open("r") as jf:
-        loaded_logger = json.load(jf, cls=ShellLoggerDecoder)
-
-    # Finalize the loaded `ShellLogger` object.
-    loaded_logger.finalize()
-
-    # Load the new HTML file's contents and compare.
-    assert html_file.exists()
-    with html_file.open("r") as hf:
-        new_html = hf.read()
-    print(f"New Read: {html_file.resolve()}")
-    assert original_html == new_html
-
-
-def test_under_stress() -> None:
-    """Test that all is well when handling lots of output."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    cmd = (
-        "dd if=/dev/urandom bs=1024 count=262144 | "
-        "LC_ALL=C tr -c '[:print:]' '*' ; sleep 1"
-    )
-    msg = "Get 256 MB of stdout from /dev/urandom"
-    logger.log(msg, cmd)
-    assert logger.log_book[0]["returncode"] == 0
-
-
-def test_heredoc() -> None:
-    """Ensure that heredocs in the command to be executed work."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    cmd = "bash << EOF\necho hello\nEOF"
-    msg = "Test out a heredoc"
-    result = logger.log(msg, cmd)
-    assert result["return_code"] == 0
-
-
-def test_devnull_stdin() -> None:
-    """Ensure ``stdin`` is redirected to ``/dev/null`` by default."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    cmd = "cat"
-    msg = "Make sure stdin is redirected to /dev/null by default"
-    result = logger.log(msg, cmd)
-    assert result["return_code"] == 0
-
-
-def test_syntax_error() -> None:
-    """Ensure syntax errors are handled appropriately."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    cmd = "echo (this is a syntax error"
-    msg = "Test out a syntax error"
-    with pytest.raises(RuntimeError) as excinfo:
-        logger.log(msg, cmd)
-    assert "There was a problem running the command" in excinfo.value.args[0]
-
-
-@pytest.mark.skipif(psutil is None, reason="`psutil` is unavailable")
-def test_logger_does_not_store_stdout_string_by_default() -> None:
-    """Ensure we don't hold a commands ``stdout`` in memory by default."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    cmd = (
-        "dd if=/dev/urandom bs=1024 count=262144 | "
-        "LC_ALL=C tr -c '[:print:]' '*' ; sleep 1"
-    )
-    msg = "Get 256 MB of stdout from /dev/urandom"
-    logger.log(msg, cmd)
-    mem_usage = psutil.Process().memory_info().rss
-    bytes_in_128_mb = 134_217_728
-    assert mem_usage < bytes_in_128_mb
-    logger.log(msg, cmd, return_info=True)
-    mem_usage = psutil.Process().memory_info().rss
-    assert mem_usage > bytes_in_128_mb
-
-
-@pytest.mark.skipif(
-    os.uname().sysname == "Darwin", reason="`ltrace` doesn't exist for Darwin"
-)
-def test_logger_does_not_store_trace_string_by_default() -> None:
-    """Ensure we don't keep trace output in memory by default."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    logger.log("echo hello", "echo hello", cwd=Path.cwd(), trace="ltrace")
-    assert logger.log_book[0]["trace"] is None
-    logger.log(
-        "echo hello",
-        "echo hello",
-        cwd=Path.cwd(),
-        return_info=True,
-        trace="ltrace",
-    )
-    assert logger.log_book[1]["trace"] is not None
-
-
-def test_stdout() -> None:
-    """Ensure printing to ``stdout`` works."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    assert logger._run(":").stdout == ""
-    assert logger._run("echo hello").stdout == "hello\n"
-
-
-def test_returncode_no_op() -> None:
-    """Ensure the return code for the `:` command is 0."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    assert logger._run(":").returncode == 0
-
-
-def test_args() -> None:
-    """Ensure we accurately capture the command that was run."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    assert logger._run("echo hello").args == "echo hello"
-
-
-def test_stderr() -> None:
-    """Ensure we accurately capture ``stderr``."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    command = "echo hello 1>&2"
-    assert logger._run(command).stderr == "hello\n"
-    assert logger._run(command).stdout == ""
-
-
-def test_timing() -> None:
-    """Ensure we accurately capture the wall clock time of a command."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    command = "sleep 1"
-    if os.name == "posix":
-        command = "sleep 1"
-    else:
-        print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
-    result = logger._run(command)
-    min_time, max_time = 1000, 2000
-    assert result.wall >= min_time
-    assert result.wall < max_time
-    assert result.finish >= result.start
-
-
-def test_auxiliary_data() -> None:
-    """
-    Ensure auxiliary data is captured.
-
-    Ensure we accurately capture all the auxiliary data when executing a
-    command.
-    """
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    result = logger._run("pwd")
-    assert result.pwd == result.stdout.strip()
-    result = logger._run(":")
-    assert "PATH=" in result.environment
-    assert logger._run("hostname").stdout.strip() == result.hostname
-    assert logger._run("whoami").stdout.strip() == result.user
-    if os.name == "posix":
-        valid_umask_lengths = [3, 4]
-        assert len(result.umask) in valid_umask_lengths
-        assert logger._run("id -gn").stdout.strip() == result.group
-        assert logger._run("printenv SHELL").stdout.strip() == result.shell
-        assert logger._run("ulimit -a").stdout == result.ulimit
-    else:
-        print(
-            f"Warning: os.name is not 'posix': {os.name}; umask, group, "
-            "shell, and ulimit not tested."
-        )
-
-
-def test_working_directory() -> None:
-    """
-    Ensure the working directory is captured.
-
-    Ensure we accurately capture the working directory when executing a
-    command.
-    """
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    command = "pwd"
-    directory = "/tmp"
-    if os.name != "posix":
-        print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
-    result = logger._run(command, pwd=directory)
-    assert result.stdout.strip() == directory
-    assert result.pwd == directory
-
-
-def test_trace() -> None:
-    """Ensure we accurately capture trace output."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    if os.uname().sysname == "Linux":
-        result = logger._run("echo letter", trace="ltrace")
-        assert 'getenv("POSIXLY_CORRECT")' in result.trace
-        echo_location = logger._run("which echo").stdout.strip()
-        result = logger._run("echo hello", trace="strace")
-        assert f'execve("{echo_location}' in result.trace
-    else:
-        print(
-            f"Warning: uname is not 'Linux': {os.uname()}; strace/ltrace not "
-            "tested."
-        )
-
-
-def test_trace_expression() -> None:
-    """Ensure specifying a trace expression works correctly."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    if os.uname().sysname == "Linux":
-        result = logger._run("echo hello", trace="ltrace", expression="getenv")
-        assert 'getenv("POSIXLY_CORRECT")' in result.trace
-        expected_newlines = 2
-        assert result.trace.count("\n") == expected_newlines
-    else:
-        print(
-            f"Warning: uname is not 'Linux': {os.uname()}; ltrace expression "
-            "not tested."
-        )
-
-
-def test_trace_summary() -> None:
-    """Ensure requesting a trace summary works correctly."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    if os.uname().sysname == "Linux":
-        result = logger._run("echo hello", trace="ltrace", summary=True)
-        assert 'getenv("POSIXLY_CORRECT")' not in result.trace
-        assert "getenv" in result.trace
-        echo_location = logger._run("which echo").stdout.strip()
-        result = logger._run("echo hello", trace="strace", summary=True)
-        assert f'execve("{echo_location}' not in result.trace
-        assert "execve" in result.trace
-    else:
-        print(
-            f"Warning: uname is not 'Linux': {os.uname()}; strace/ltrace "
-            "summary not tested."
-        )
-
-
-def test_trace_expression_and_summary() -> None:
-    """Ensure specifying a trace expression and requesting a summary works."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    if os.uname().sysname == "Linux":
-        echo_location = logger._run("which echo").stdout.strip()
-        result = logger._run(
-            "echo hello", trace="strace", expression="execve", summary=True
-        )
-        assert f'execve("{echo_location}' not in result.trace
-        assert "execve" in result.trace
-        assert "getenv" not in result.trace
-        result = logger._run(
-            "echo hello", trace="ltrace", expression="getenv", summary=True
-        )
-        assert 'getenv("POSIXLY_CORRECT")' not in result.trace
-        assert "getenv" in result.trace
-        assert "strcmp" not in result.trace
-    else:
-        print(
-            f"Warning: uname is not 'Linux': {os.uname()}; strace/ltrace "
-            "expression+summary not tested."
-        )
-
-
-def test_stats() -> None:
-    """
-    Ensure capturing CPU, memory, and disk statistics works correctly.
-
-    Todo:
-        Ensure disk statistics are collected at the specified interval
-        on RHEL.
-    """
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    measure = ["cpu", "memory", "disk"]
-    result = logger._run("sleep 2", measure=measure, interval=0.1)
-    min_results, max_results = 1, 30
-    assert len(result.stats["memory"]) > min_results
-    assert len(result.stats["memory"]) < max_results
-    assert len(result.stats["cpu"]) > min_results
-    assert len(result.stats["cpu"]) < max_results
-    if os.name == "posix" and distro.name() != "Red Hat Enterprise Linux":
-        assert len(result.stats["disk"]["/"]) > min_results
-        assert len(result.stats["disk"]["/"]) < max_results
-    else:
-        print(
-            f"Warning: os.name is not 'posix': {os.name}; disk usage not "
-            "fully tested."
-        )
-
-
-def test_trace_and_stats() -> None:
-    """
-    Ensure trace and multiple statistics work together.
-
-    Ensure both tracing a command and capturing multiple statistics work
-    together.
-
-    Todo:
-        Ensure disk statistics are collected at the specified interval
-        on RHEL.
-    """
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    if os.uname().sysname == "Linux":
-        measure = ["cpu", "memory", "disk"]
-        result = logger._run(
-            "sleep 1",
-            measure=measure,
-            interval=0.1,
-            trace="ltrace",
-            expression="setlocale",
-            summary=True,
-        )
-        assert "setlocale" in result.trace
-        assert "sleep" not in result.trace
-        min_results, max_results = 5, 50
-        assert len(result.stats["memory"]) > min_results
-        assert len(result.stats["memory"]) < max_results
-        assert len(result.stats["cpu"]) > min_results
-        assert len(result.stats["cpu"]) < max_results
-        if distro.name() != "Red Hat Enterprise Linux":
-            assert len(result.stats["disk"]["/"]) > min_results
-            assert len(result.stats["disk"]["/"]) < max_results
-    else:
-        print(
-            f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested."
-        )
-
-
-def test_trace_and_stat() -> None:
-    """
-    Ensure trace and a single statistic work together.
-
-    Ensure both tracing a command and capturing a single statistic work
-    together.
-    """
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    if os.uname().sysname == "Linux":
-        result = logger._run(
-            "sleep 1",
-            measure=["cpu"],
-            interval=0.1,
-            trace="ltrace",
-            expression="setlocale",
-            summary=True,
-        )
-        assert "setlocale" in result.trace
-        assert "sleep" not in result.trace
-        assert result.stats.get("memory") is None
-        assert result.stats.get("disk") is None
-        assert result.stats.get("cpu") is not None
-    else:
-        print(
-            f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested."
-        )
-
-
-@pytest.mark.skipif(
-    os.uname().sysname == "Darwin",
-    reason="`ltrace`/`strace` don't exist for Darwin",
-)
-@pytest.mark.skip(reason="Not sure it's worth it to fix this or not")
-def test_set_env_trace() -> None:
-    """Ensure environment variables work with trace."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    result = logger._run("TEST_ENV=abdc env | grep TEST_ENV", trace="ltrace")
-    assert "TEST_ENV=abdc" in result.stdout
-    result = logger._run("TEST_ENV=abdc env | grep TEST_ENV", trace="strace")
-    assert "TEST_ENV=abdc" in result.stdout
-
-
-def test_log_book_trace_and_stats() -> None:
-    """
-    Ensure trace and statistics are accurately captured in the log book.
-
-    Todo:
-        Ensure disk statistics are collected at the specified interval
-        on RHEL.
-    """
-    if os.uname().sysname == "Linux":
-        logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-        measure = ["cpu", "memory", "disk"]
-        logger.log(
-            "Sleep",
-            "sleep 1",
-            return_info=True,
-            measure=measure,
-            interval=0.1,
-            trace="ltrace",
-            expression="setlocale",
-            summary=True,
-        )
-        assert "setlocale" in logger.log_book[0]["trace"]
-        assert "sleep" not in logger.log_book[0]["trace"]
-        min_results, max_results = 5, 50
-        assert len(logger.log_book[0]["stats"]["memory"]) > min_results
-        assert len(logger.log_book[0]["stats"]["memory"]) < max_results
-        assert len(logger.log_book[0]["stats"]["cpu"]) > min_results
-        assert len(logger.log_book[0]["stats"]["cpu"]) < max_results
-        if distro.name() != "Red Hat Enterprise Linux":
-            assert len(logger.log_book[0]["stats"]["disk"]["/"]) > min_results
-            assert len(logger.log_book[0]["stats"]["disk"]["/"]) < max_results
-    else:
-        print(
-            f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested."
-        )
-
-
-def test_change_pwd() -> None:
-    """Ensure changing directories affects subsequent calls."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    pwd_command = "pwd"
-    directory1 = "/"
-    directory2 = "/tmp"
-    if os.name != "posix":
-        print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
-    logger._run(f"cd {directory1}")
-    result = logger._run(pwd_command)
-    assert result.stdout.strip() == directory1
-    assert result.pwd == directory1
-    logger._run(f"cd {directory2}")
-    result = logger._run(pwd_command)
-    assert result.stdout.strip() == directory2
-    assert result.pwd == directory2
-
-
-def test_returncode() -> None:
-    """Ensure we get the expected return code when a command fails."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    command = "false"
-    expected_returncode = 1
-    if os.name != "posix":
-        print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
-    result = logger._run(command)
-    assert result.returncode == expected_returncode
-
-
-def test_sgr_gets_converted_to_html() -> None:
-    """
-    Ensure SGR to HTML translation works.
-
-    Ensure Select Graphic Rendition (SGR) codes get accurately
-    translated to valid HTML/CSS.
-    """
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    logger.print("\x1b[31mHello\x1b[0m")
-    logger.print("\x1b[31;43m\x1b[4mthere\x1b[0m")
-    logger.print("\x1b[38;5;196m\x1b[48;5;232m\x1b[4mmr.\x1b[0m logger")
-    logger.print(
-        "\x1b[38;2;96;140;240m\x1b[48;2;240;140;10mmrs.\x1b[0m logger"
-    )
-    logger.finalize()
-
-    # Load the HTML file and make sure it checks out.
-    html_file = logger.stream_dir / f"{logger.name}.html"
-    assert html_file.exists()
-    with html_file.open("r") as hf:
-        html_text = hf.read()
-    assert "\x1b" not in html_text
-    assert ">Hello" in html_text
-    assert ">there" in html_text
-    assert ">mr. logger" in html_text
-    assert "color: rgb(255, 0, 0)" in html_text
-    assert "background-color: rgb(" in html_text
-    assert ">mrs. logger" in html_text
-    assert "color: rgb(96, 140, 240)" in html_text
-    assert "background-color: rgb(240, 140, 10)" in html_text
-
-
-def test_html_print(capsys: CaptureFixture) -> None:
-    """
-    Ensure :func:`html_print` doesn't print to the console.
-
-    Ensure the :func:`print` method prints to both the HTML log and the
-    console, but the :func:`html_print` method only prints to the log
-    file.
-
-    Parameters:
-        capsys:  A fixture for capturing the ``stdout``.
-    """
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    logger.html_print(
-        "The quick brown fox jumps over the lazy dog.", msg_title="Brown Fox"
-    )
-    logger.print("The quick orange zebra jumps over the lazy dog.")
-    out, err = capsys.readouterr()
-    logger.finalize()
-
-    # Load the HTML file and make sure it checks out.
-    html_file = logger.stream_dir / f"{logger.name}.html"
-    assert html_file.exists()
-    with html_file.open("r") as hf:
-        html_text = hf.read()
-    assert "brown fox" not in out
-    assert "brown fox" not in err
-    assert "Brown Fox" not in out
-    assert "Brown Fox" not in err
-    assert "brown fox" in html_text
-    assert "Brown Fox" in html_text
-    assert "orange zebra" not in err
-    assert "orange zebra" in out
-    assert "orange zebra" in html_text
-
-
-def test_append_mode() -> None:
-    """Ensure we're able to append to a previously generated log file."""
-    logger1 = ShellLogger(stack()[0][3] + "_1", log_dir=Path.cwd())
-    logger1.log("Print HELLO to stdout", "echo HELLO")
-    logger1.print("Printed once to stdout")
-    logger1.html_print("Printed ONCE to STDOUT")
-    logger1.finalize()
-
-    logger2 = ShellLogger.append(logger1.html_file)
-    logger2.log("Print THERE to stdout", "echo THERE")
-    logger2.print("Printed twice to stdout")
-    logger2.html_print("Printed TWICE to STDOUT")
-    logger2.finalize()
-
-    logger3 = ShellLogger.append(logger2.log_dir)
-    logger3.log("Print LOGGER to stdout", "echo LOGGER")
-    logger3.print("Printed thrice to stdout")
-    logger3.html_print("Printed THRICE to STDOUT")
-    logger3.finalize()
-
-    logger4 = ShellLogger.append(logger3.stream_dir)
-    logger4.log("Print !!! to stdout", "echo '!!!'")
-    logger4.print("Printed finally to stdout")
-    logger4.html_print("Printed FINALLY to STDOUT")
-    logger4.finalize()
-
-    logger5 = ShellLogger.append(logger4.stream_dir / f"{logger4.name}.json")
-    logger5.log("Print 111 to stdout", "echo '111'")
-    logger5.print("Printed for real to stdout")
-    logger5.html_print("Printed FOR REAL to STDOUT")
-    logger5.finalize()
-
-    # Load the HTML file and ensure it checks out.
-    html_file = logger1.stream_dir / f"{logger1.name}.html"
-    assert html_file.exists()
-    with html_file.open("r") as hf:
-        html_text = hf.read()
-    assert "once" in html_text
-    assert "ONCE" in html_text
-    assert "HELLO" in html_text
-    assert "twice" in html_text
-    assert "TWICE" in html_text
-    assert "THERE" in html_text
-    assert "thrice" in html_text
-    assert "THRICE" in html_text
-    assert "LOGGER" in html_text
-    assert "finally" in html_text
-    assert "FINALLY" in html_text
-    assert "!!!" in html_text
-    assert "for real" in html_text
-    assert "FOR REAL" in html_text
-    assert "111" in html_text
-
-
-def test_invalid_decodings() -> None:
-    """Ensure we appropriately handle invalid bytes when decoding output."""
-    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-    result = logger.log(
-        "Print invalid start byte for bytes decode()",
-        "printf '\\xFDHello\\n'",
-        return_info=True,
-    )
-    assert result["stdout"] == "Hello\n"
+# @pytest.fixture(autouse=True)
+# def _use_tmpdir(monkeypatch: MonkeyPatch, tmpdir: Path) -> None:
+#     """
+#     Use a temporary directory for all tests.
+
+#     Parameters:
+#         monkeypatch:  The ``MonkeyPatch`` fixture.
+#         tmpdir:  The temporary directory to use.
+#     """
+#     monkeypatch.chdir(tmpdir)
+
+
+# @pytest.fixture()
+# def shell_logger() -> ShellLogger:
+#     """
+#     Pre-populate a :class:`ShellLogger` for use in the tests.
+
+#     This fixture creates a :class:`ShellLogger` object with some sample
+#     data to be used in tests.  It first creates a sample
+#     :class:`ShellLogger` object.  Then it logs a command (whose
+#     ``stdout`` is ``'Hello world'`` and ``stderr`` is ``'Hello world
+#     error'``).  Next, it adds a child :class:`ShellLogger` object and
+#     prints something using that child logger.
+
+#     Returns:
+#         The parent :class:`ShellLogger` object described above.
+#     """
+#     # Initialize a parent `ShellLogger`.
+#     parent = ShellLogger("Parent", log_dir=Path.cwd())
+
+#     # Run the command.
+#     #                      `stdout`        ;               `stderr`
+#     cmd = (
+#         "sleep 1; echo 'Hello world out'; sleep 1; echo 'Hello world error' "
+#         "1>&2"
+#     )
+#     measure = ["cpu", "memory", "disk"]
+#     kwargs = {"measure": measure, "return_info": True, "interval": 0.1}
+#     if os.uname().sysname == "Linux":
+#         kwargs.update(
+#             {"trace": "ltrace", "expression": "setlocale", "summary": True}
+#         )
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested."
+#         )
+#     parent.log("test cmd", cmd, cwd=Path.cwd(), **kwargs)
+#     parent.print("This is a message")
+
+#     # Add a child and run some commands.
+#     child = parent.add_child("Child")
+#     child.print("Hello world child")
+#     child.log("Test out HTML characters", "echo ' &\"'\"'\"")
+#     child.log("ls", "ls")
+#     child.print("Hello again child")
+
+#     # Add more to the parent and return the object.
+#     parent.print("This is another message")
+#     return parent
+
+
+# def test_initialization_creates_stream_dir() -> None:
+#     """
+#     Ensure the stream directory is created.
+
+#     Verify the initialization of a parent :class:`ShellLogger` object
+#     creates a temporary directory
+#     (``log_dir/%Y-%m-%d_%H%M%S``) if not already created.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     timestamp = logger.init_time.strftime("%Y-%m-%d_%H.%M.%S.%f")
+#     assert len(list(Path.cwd().glob(f"{timestamp}_*"))) == 1
+
+
+# def test_initialization_creates_html_file() -> None:
+#     """
+#     Ensure the HTML file is created.
+
+#     Verify the initialization of a parent :class:`ShellLogger` object
+#     creates a starting HTML file in the :attr:`log_dir`.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     timestamp = logger.init_time.strftime("%Y-%m-%d_%H.%M.%S.%f")
+#     streamm_dir = next(Path.cwd().glob(f"{timestamp}_*"))
+#     assert (streamm_dir / f"{stack()[0][3]}.html").exists()
+
+
+# def test_log_method_creates_tmp_stdout_stderr_files(
+#     shell_logger: ShellLogger,
+# ) -> None:
+#     """
+#     Ensure output files are created.
+
+#     Verify that logging a command will create files in the
+#     :class:`ShellLogger` object's :attr:`stream_dir` corresponding to
+#     the ``stdout`` and ``stderr`` of the command.
+
+#     Parameters:
+#         shell_logger:  A pre-populated :class:`ShellLogger` object.
+#     """
+#     # Get the paths for the `stdout`/`stderr` files.
+#     cmd_id = shell_logger.log_book[0]["cmd_id"]
+#     cmd_ts = shell_logger.log_book[0]["timestamp"]
+#     stdout_file = shell_logger.stream_dir / f"{cmd_ts}_{cmd_id}_stdout"
+#     stderr_file = shell_logger.stream_dir / f"{cmd_ts}_{cmd_id}_stderr"
+#     assert stdout_file.exists()
+#     assert stderr_file.exists()
+#     print(f"{stdout_file}")
+#     print(f"{stderr_file}")
+
+#     # Make sure the information written to these files is correct.
+#     with stdout_file.open("r") as out, stderr_file.open("r") as err:
+#         out_txt = out.readline()
+#         err_txt = err.readline()
+#         assert "Hello world out" in out_txt
+#         assert "Hello world error" in err_txt
+
+
+# @pytest.mark.parametrize("return_info", [True, False])
+# def test_log_method_return_info_works_correctly(
+#     return_info: bool,  # noqa: FBT001
+# ) -> None:
+#     """
+#     Ensure ``stdout``/``stderr`` are returned when requested.
+
+#     Verify that when ``return_info=True``, we receive a dictionary that
+#     contains the ``stdout`` and ``stderr`` of the command, as well as
+#     the ``return_code``, and when ``return_info=False``, we receive the
+#     ``return_code``, but ``stdout`` and ``stderr`` are ``None``.
+
+#     Parameters:
+#         return_info:  Whether or not to return the
+#             ``stdout``/``stderr``.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+
+#     #           `stdout`         ;      `stderr`
+#     cmd = "echo 'Hello world out'; echo 'Hello world error' 1>&2"
+#     result = logger.log(
+#         "test cmd",
+#         cmd,
+#         cwd=Path.cwd(),
+#         return_info=return_info,
+#     )
+#     if return_info:
+#         assert "Hello world out" in result["stdout"]
+#         assert "Hello world error" in result["stderr"]
+#         assert result["return_code"] == 0
+#     else:
+#         assert result["stdout"] is None
+#         assert result["stderr"] is None
+#         assert result["return_code"] == 0
+
+
+# @pytest.mark.parametrize("live_stdout", [True, False])
+# @pytest.mark.parametrize("live_stderr", [True, False])
+# def test_log_method_live_stdout_stderr_works_correctly(
+#     capsys: CaptureFixture,
+#     live_stdout: bool,  # noqa: FBT001
+#     live_stderr: bool,  # noqa: FBT001
+# ) -> None:
+#     """
+#     Ensure live streaming of ``stdout``/``stderr`` works.
+
+#     Verify that the ``live_stdout`` and ``live_stdout`` flags work as
+#     expected for the :func:`log` method.
+
+#     Parameters:
+#         capsys: A fixture to capture ``stdout``/``stderr``.
+#         live_stdout:  Whether or not to capture ``stdout`` while running
+#             the :func:`log` command.
+#         live_stderr:  Whether or not to capture ``stderr`` while running
+#             the :func:`log` command.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     cmd = "echo 'Hello world out'; echo 'Hello world error' 1>&2"
+#     logger.log(
+#         "test cmd",
+#         cmd,
+#         cwd=Path.cwd(),
+#         live_stdout=live_stdout,
+#         live_stderr=live_stderr,
+#     )
+#     out, err = capsys.readouterr()
+#     if live_stdout:
+#         assert re.search(r"^Hello world out(\r)?\n", out) is not None
+#     else:
+#         assert re.search(r"^Hello world out(\r)?\n", out) is None
+#     if live_stderr:
+#         assert re.search(r"^Hello world error(\r)?\n", err) is not None
+#     else:
+#         assert re.search(r"^Hello world error(\r)?\n", err) is None
+
+
+# def test_child_logger_duration_displayed_correctly_in_html(
+#     shell_logger: ShellLogger,
+# ) -> None:
+#     """
+#     Ensure child logger durations displays correctly.
+
+#     Verify that the overview of child loggers in the HTML file displays
+#     the correct child :class:`ShellLogger` duration, not the entire
+#     log's duration.
+
+#     Parameters:
+#         shell_logger:  A pre-populated :class:`ShellLogger` object.
+#     """
+#     child2 = shell_logger.add_child("Child 2")
+#     child2.log("Wait 0.005s", "sleep 0.005")
+#     child3 = shell_logger.add_child("Child 3")
+#     child3.log("Wait 0.006s", "sleep 0.006")
+#     shell_logger.finalize()
+#     with shell_logger.html_file.open("r") as hf:
+#         html_text = hf.read()
+#     assert child2.duration is not None
+#     assert f"Duration: {child2.duration}" in html_text
+#     assert child3.duration is not None
+#     assert f"Duration: {child3.duration}" in html_text
+
+
+# def test_finalize_creates_json_with_correct_information(
+#     shell_logger: ShellLogger,
+# ) -> None:
+#     """
+#     Ensure :func:`finalize` creates a JSON file with the proper data.
+
+#     Parameters:
+#         shell_logger:  A pre-populated :class:`ShellLogger` object.
+#     """
+#     shell_logger.finalize()
+
+#     # Load from JSON.
+#     json_file = shell_logger.stream_dir / "Parent.json"
+#     assert json_file.exists()
+#     with json_file.open("r") as jf:
+#         loaded_logger = json.load(jf, cls=ShellLoggerDecoder)
+
+#     # Parent `ShellLogger`.
+#     assert shell_logger.log_dir == loaded_logger.log_dir
+#     assert shell_logger.stream_dir == loaded_logger.stream_dir
+#     assert shell_logger.html_file == loaded_logger.html_file
+#     assert shell_logger.indent == loaded_logger.indent
+#     assert shell_logger.name == loaded_logger.name
+#     assert shell_logger.init_time == loaded_logger.init_time
+#     assert shell_logger.done_time == loaded_logger.done_time
+#     assert shell_logger.log_book[0] == loaded_logger.log_book[0]
+
+#     # Child `ShellLogger`.
+#     child = shell_logger.log_book[2]
+#     loaded_child = loaded_logger.log_book[2]
+#     assert child.log_dir == loaded_child.log_dir
+#     assert child.stream_dir == loaded_child.stream_dir
+#     assert child.html_file == loaded_child.html_file
+#     assert child.indent == loaded_child.indent
+#     assert child.name == loaded_child.name
+#     assert child.init_time == loaded_child.init_time
+#     assert child.done_time == loaded_child.done_time
+#     assert child.log_book[0] == loaded_child.log_book[0]
+
+
+# def test_finalize_creates_html_with_correct_information(
+#     shell_logger: ShellLogger,
+# ) -> None:
+#     """
+#     Ensure :func:`finalize` creates a HTML file with the proper data.
+
+#     Parameters:
+#         shell_logger:  A pre-populated :class:`ShellLogger` object.
+#     """
+#     shell_logger.finalize()
+
+#     # Load the HTML file.
+#     html_file = shell_logger.stream_dir / "Parent.html"
+#     assert html_file.exists()
+#     with html_file.open("r") as hf:
+#         html_text = hf.read()
+
+#     # Ensure the command information is present.
+#     assert ">test cmd {shell_logger.log_book[0]['timestamp']}" in html_text
+#     assert (
+#         "Command: 
sleep 1; echo 'Hello world out'; sleep 1; "
+#         "echo 'Hello world error' 1>&2"
+#     ) in html_text
+#     assert f"CWD: {Path.cwd()}" in html_text
+#     assert "Return Code: 0" in html_text
+
+#     # Check to see that the print statement information is there.
+#     assert "Hello world child" in html_text
+#     if os.uname().sysname == "Linux":
+#         assert "traceMemory Usage' in html_text
+#     assert "" in html_text
+#     assert 'class="card-title">CPU Usage' in html_text
+#     assert 'class="card-title">Used Space on /' in html_text
+#     assert "Environment" in html_text
+#     assert "User:" in html_text
+#     assert "Group:" in html_text
+#     assert "Shell:" in html_text
+#     assert "umask:" in html_text
+#     assert "ulimit None:
+#     """
+#     Ensure :func:`finalize` creates the appropriate symlink.
+
+#     Verify that the :func:`finalize` method symlinks
+#     ``log_dir/html_file`` to ``streamm_dir/html_file``.
+
+#     Parameters:
+#         shell_logger:  A pre-populated :class:`ShellLogger` object.
+#     """
+#     shell_logger.finalize()
+
+#     # Load the HTML file.
+#     html_file = shell_logger.stream_dir / "Parent.html"
+#     html_symlink = shell_logger.log_dir / "Parent.html"
+#     assert html_file.exists()
+#     assert html_symlink.exists()
+#     assert html_symlink.resolve() == html_file
+
+
+# def test_json_file_can_reproduce_html_file(
+#     shell_logger: ShellLogger,
+# ) -> None:
+#     """
+#     Ensure the JSON file can regenerate the HTML.
+
+#     Verify that a JSON file can properly recreate the original HTML file
+#     created when :func:`finalize` is called.
+
+#     Parameters:
+#         shell_logger:  A pre-populated :class:`ShellLogger` object.
+#     """
+#     shell_logger.finalize()
+
+#     # Load the original HTML file's contents.
+#     html_file = shell_logger.log_dir / "Parent.html"
+#     assert html_file.exists()
+#     with html_file.open("r") as hf:
+#         original_html = hf.read()
+
+#     # Delete the HTML file.
+#     html_file.unlink()
+
+#     # Load the JSON data.
+#     json_file = shell_logger.stream_dir / "Parent.json"
+#     assert json_file.exists()
+#     with json_file.open("r") as jf:
+#         loaded_logger = json.load(jf, cls=ShellLoggerDecoder)
+
+#     # Finalize the loaded `ShellLogger` object.
+#     loaded_logger.finalize()
+
+#     # Load the new HTML file's contents and compare.
+#     assert html_file.exists()
+#     with html_file.open("r") as hf:
+#         new_html = hf.read()
+#     print(f"New Read: {html_file.resolve()}")
+#     assert original_html == new_html
+
+
+# def test_under_stress() -> None:
+#     """Test that all is well when handling lots of output."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     cmd = (
+#         "dd if=/dev/urandom bs=1024 count=262144 | "
+#         "LC_ALL=C tr -c '[:print:]' '*' ; sleep 1"
+#     )
+#     msg = "Get 256 MB of stdout from /dev/urandom"
+#     logger.log(msg, cmd)
+#     assert logger.log_book[0]["returncode"] == 0
+
+
+# def test_heredoc() -> None:
+#     """Ensure that heredocs in the command to be executed work."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     cmd = "bash << EOF\necho hello\nEOF"
+#     msg = "Test out a heredoc"
+#     result = logger.log(msg, cmd)
+#     assert result["return_code"] == 0
+
+
+# def test_devnull_stdin() -> None:
+#     """Ensure ``stdin`` is redirected to ``/dev/null`` by default."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     cmd = "cat"
+#     msg = "Make sure stdin is redirected to /dev/null by default"
+#     result = logger.log(msg, cmd)
+#     assert result["return_code"] == 0
+
+
+# def test_syntax_error() -> None:
+#     """Ensure syntax errors are handled appropriately."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     cmd = "echo (this is a syntax error"
+#     msg = "Test out a syntax error"
+#     with pytest.raises(RuntimeError) as excinfo:
+#         logger.log(msg, cmd)
+#     assert "There was a problem running the command" in excinfo.value.args[0]
+
+
+# @pytest.mark.skipif(psutil is None, reason="`psutil` is unavailable")
+# def test_logger_does_not_store_stdout_string_by_default() -> None:
+#     """Ensure we don't hold a commands ``stdout`` in memory by default."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     cmd = (
+#         "dd if=/dev/urandom bs=1024 count=262144 | "
+#         "LC_ALL=C tr -c '[:print:]' '*' ; sleep 1"
+#     )
+#     msg = "Get 256 MB of stdout from /dev/urandom"
+#     logger.log(msg, cmd)
+#     mem_usage = psutil.Process().memory_info().rss
+#     bytes_in_128_mb = 134_217_728
+#     assert mem_usage < bytes_in_128_mb
+#     logger.log(msg, cmd, return_info=True)
+#     mem_usage = psutil.Process().memory_info().rss
+#     assert mem_usage > bytes_in_128_mb
+
+
+# @pytest.mark.skipif(
+#     os.uname().sysname == "Darwin", reason="`ltrace` doesn't exist for Darwin"
+# )
+# def test_logger_does_not_store_trace_string_by_default() -> None:
+#     """Ensure we don't keep trace output in memory by default."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     logger.log("echo hello", "echo hello", cwd=Path.cwd(), trace="ltrace")
+#     assert logger.log_book[0]["trace"] is None
+#     logger.log(
+#         "echo hello",
+#         "echo hello",
+#         cwd=Path.cwd(),
+#         return_info=True,
+#         trace="ltrace",
+#     )
+#     assert logger.log_book[1]["trace"] is not None
+
+
+# def test_stdout() -> None:
+#     """Ensure printing to ``stdout`` works."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     assert logger._run(":").stdout == ""
+#     assert logger._run("echo hello").stdout == "hello\n"
+
+
+# def test_returncode_no_op() -> None:
+#     """Ensure the return code for the `:` command is 0."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     assert logger._run(":").returncode == 0
+
+
+# def test_args() -> None:
+#     """Ensure we accurately capture the command that was run."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     assert logger._run("echo hello").args == "echo hello"
+
+
+# def test_stderr() -> None:
+#     """Ensure we accurately capture ``stderr``."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     command = "echo hello 1>&2"
+#     assert logger._run(command).stderr == "hello\n"
+#     assert logger._run(command).stdout == ""
+
+
+# def test_timing() -> None:
+#     """Ensure we accurately capture the wall clock time of a command."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     command = "sleep 1"
+#     if os.name == "posix":
+#         command = "sleep 1"
+#     else:
+#         print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
+#     result = logger._run(command)
+#     min_time, max_time = 1000, 2000
+#     assert result.wall >= min_time
+#     assert result.wall < max_time
+#     assert result.finish >= result.start
+
+
+# def test_auxiliary_data() -> None:
+#     """
+#     Ensure auxiliary data is captured.
+
+#     Ensure we accurately capture all the auxiliary data when executing a
+#     command.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     result = logger._run("pwd")
+#     assert result.pwd == result.stdout.strip()
+#     result = logger._run(":")
+#     assert "PATH=" in result.environment
+#     assert logger._run("hostname").stdout.strip() == result.hostname
+#     assert logger._run("whoami").stdout.strip() == result.user
+#     if os.name == "posix":
+#         valid_umask_lengths = [3, 4]
+#         assert len(result.umask) in valid_umask_lengths
+#         assert logger._run("id -gn").stdout.strip() == result.group
+#         assert logger._run("printenv SHELL").stdout.strip() == result.shell
+#         assert logger._run("ulimit -a").stdout == result.ulimit
+#     else:
+#         print(
+#             f"Warning: os.name is not 'posix': {os.name}; umask, group, "
+#             "shell, and ulimit not tested."
+#         )
+
+
+# def test_working_directory() -> None:
+#     """
+#     Ensure the working directory is captured.
+
+#     Ensure we accurately capture the working directory when executing a
+#     command.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     command = "pwd"
+#     directory = "/tmp"
+#     if os.name != "posix":
+#         print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
+#     result = logger._run(command, pwd=directory)
+#     assert result.stdout.strip() == directory
+#     assert result.pwd == directory
+
+
+# def test_trace() -> None:
+#     """Ensure we accurately capture trace output."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     if os.uname().sysname == "Linux":
+#         result = logger._run("echo letter", trace="ltrace")
+#         assert 'getenv("POSIXLY_CORRECT")' in result.trace
+#         echo_location = logger._run("which echo").stdout.strip()
+#         result = logger._run("echo hello", trace="strace")
+#         assert f'execve("{echo_location}' in result.trace
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; strace/ltrace not "
+#             "tested."
+#         )
+
+
+# def test_trace_expression() -> None:
+#     """Ensure specifying a trace expression works correctly."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     if os.uname().sysname == "Linux":
+#         result = logger._run("echo hello", trace="ltrace", expression="getenv")
+#         assert 'getenv("POSIXLY_CORRECT")' in result.trace
+#         expected_newlines = 2
+#         assert result.trace.count("\n") == expected_newlines
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; ltrace expression "
+#             "not tested."
+#         )
+
+
+# def test_trace_summary() -> None:
+#     """Ensure requesting a trace summary works correctly."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     if os.uname().sysname == "Linux":
+#         result = logger._run("echo hello", trace="ltrace", summary=True)
+#         assert 'getenv("POSIXLY_CORRECT")' not in result.trace
+#         assert "getenv" in result.trace
+#         echo_location = logger._run("which echo").stdout.strip()
+#         result = logger._run("echo hello", trace="strace", summary=True)
+#         assert f'execve("{echo_location}' not in result.trace
+#         assert "execve" in result.trace
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; strace/ltrace "
+#             "summary not tested."
+#         )
+
+
+# def test_trace_expression_and_summary() -> None:
+#     """Ensure specifying a trace expression and requesting a summary works."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     if os.uname().sysname == "Linux":
+#         echo_location = logger._run("which echo").stdout.strip()
+#         result = logger._run(
+#             "echo hello", trace="strace", expression="execve", summary=True
+#         )
+#         assert f'execve("{echo_location}' not in result.trace
+#         assert "execve" in result.trace
+#         assert "getenv" not in result.trace
+#         result = logger._run(
+#             "echo hello", trace="ltrace", expression="getenv", summary=True
+#         )
+#         assert 'getenv("POSIXLY_CORRECT")' not in result.trace
+#         assert "getenv" in result.trace
+#         assert "strcmp" not in result.trace
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; strace/ltrace "
+#             "expression+summary not tested."
+#         )
+
+
+# def test_stats() -> None:
+#     """
+#     Ensure capturing CPU, memory, and disk statistics works correctly.
+
+#     Todo:
+#         Ensure disk statistics are collected at the specified interval
+#         on RHEL.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     measure = ["cpu", "memory", "disk"]
+#     result = logger._run("sleep 2", measure=measure, interval=0.1)
+#     min_results, max_results = 1, 30
+#     assert len(result.stats["memory"]) > min_results
+#     assert len(result.stats["memory"]) < max_results
+#     assert len(result.stats["cpu"]) > min_results
+#     assert len(result.stats["cpu"]) < max_results
+#     if os.name == "posix" and distro.name() != "Red Hat Enterprise Linux":
+#         assert len(result.stats["disk"]["/"]) > min_results
+#         assert len(result.stats["disk"]["/"]) < max_results
+#     else:
+#         print(
+#             f"Warning: os.name is not 'posix': {os.name}; disk usage not "
+#             "fully tested."
+#         )
+
+
+# def test_trace_and_stats() -> None:
+#     """
+#     Ensure trace and multiple statistics work together.
+
+#     Ensure both tracing a command and capturing multiple statistics work
+#     together.
+
+#     Todo:
+#         Ensure disk statistics are collected at the specified interval
+#         on RHEL.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     if os.uname().sysname == "Linux":
+#         measure = ["cpu", "memory", "disk"]
+#         result = logger._run(
+#             "sleep 1",
+#             measure=measure,
+#             interval=0.1,
+#             trace="ltrace",
+#             expression="setlocale",
+#             summary=True,
+#         )
+#         assert "setlocale" in result.trace
+#         assert "sleep" not in result.trace
+#         min_results, max_results = 5, 50
+#         assert len(result.stats["memory"]) > min_results
+#         assert len(result.stats["memory"]) < max_results
+#         assert len(result.stats["cpu"]) > min_results
+#         assert len(result.stats["cpu"]) < max_results
+#         if distro.name() != "Red Hat Enterprise Linux":
+#             assert len(result.stats["disk"]["/"]) > min_results
+#             assert len(result.stats["disk"]["/"]) < max_results
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested."
+#         )
+
+
+# def test_trace_and_stat() -> None:
+#     """
+#     Ensure trace and a single statistic work together.
+
+#     Ensure both tracing a command and capturing a single statistic work
+#     together.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     if os.uname().sysname == "Linux":
+#         result = logger._run(
+#             "sleep 1",
+#             measure=["cpu"],
+#             interval=0.1,
+#             trace="ltrace",
+#             expression="setlocale",
+#             summary=True,
+#         )
+#         assert "setlocale" in result.trace
+#         assert "sleep" not in result.trace
+#         assert result.stats.get("memory") is None
+#         assert result.stats.get("disk") is None
+#         assert result.stats.get("cpu") is not None
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested."
+#         )
+
+
+# @pytest.mark.skipif(
+#     os.uname().sysname == "Darwin",
+#     reason="`ltrace`/`strace` don't exist for Darwin",
+# )
+# @pytest.mark.skip(reason="Not sure it's worth it to fix this or not")
+# def test_set_env_trace() -> None:
+#     """Ensure environment variables work with trace."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     result = logger._run("TEST_ENV=abdc env | grep TEST_ENV", trace="ltrace")
+#     assert "TEST_ENV=abdc" in result.stdout
+#     result = logger._run("TEST_ENV=abdc env | grep TEST_ENV", trace="strace")
+#     assert "TEST_ENV=abdc" in result.stdout
+
+
+# def test_log_book_trace_and_stats() -> None:
+#     """
+#     Ensure trace and statistics are accurately captured in the log book.
+
+#     Todo:
+#         Ensure disk statistics are collected at the specified interval
+#         on RHEL.
+#     """
+#     if os.uname().sysname == "Linux":
+#         logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#         measure = ["cpu", "memory", "disk"]
+#         logger.log(
+#             "Sleep",
+#             "sleep 1",
+#             return_info=True,
+#             measure=measure,
+#             interval=0.1,
+#             trace="ltrace",
+#             expression="setlocale",
+#             summary=True,
+#         )
+#         assert "setlocale" in logger.log_book[0]["trace"]
+#         assert "sleep" not in logger.log_book[0]["trace"]
+#         min_results, max_results = 5, 50
+#         assert len(logger.log_book[0]["stats"]["memory"]) > min_results
+#         assert len(logger.log_book[0]["stats"]["memory"]) < max_results
+#         assert len(logger.log_book[0]["stats"]["cpu"]) > min_results
+#         assert len(logger.log_book[0]["stats"]["cpu"]) < max_results
+#         if distro.name() != "Red Hat Enterprise Linux":
+#             assert len(logger.log_book[0]["stats"]["disk"]["/"]) > min_results
+#             assert len(logger.log_book[0]["stats"]["disk"]["/"]) < max_results
+#     else:
+#         print(
+#             f"Warning: uname is not 'Linux': {os.uname()}; ltrace not tested."
+#         )
+
+
+# def test_change_pwd() -> None:
+#     """Ensure changing directories affects subsequent calls."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     pwd_command = "pwd"
+#     directory1 = "/"
+#     directory2 = "/tmp"
+#     if os.name != "posix":
+#         print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
+#     logger._run(f"cd {directory1}")
+#     result = logger._run(pwd_command)
+#     assert result.stdout.strip() == directory1
+#     assert result.pwd == directory1
+#     logger._run(f"cd {directory2}")
+#     result = logger._run(pwd_command)
+#     assert result.stdout.strip() == directory2
+#     assert result.pwd == directory2
+
+
+# def test_returncode() -> None:
+#     """Ensure we get the expected return code when a command fails."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     command = "false"
+#     expected_returncode = 1
+#     if os.name != "posix":
+#         print(f"Warning: os.name is unrecognized: {os.name}; test may fail.")
+#     result = logger._run(command)
+#     assert result.returncode == expected_returncode
+
+
+# def test_sgr_gets_converted_to_html() -> None:
+#     """
+#     Ensure SGR to HTML translation works.
+
+#     Ensure Select Graphic Rendition (SGR) codes get accurately
+#     translated to valid HTML/CSS.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     logger.print("\x1b[31mHello\x1b[0m")
+#     logger.print("\x1b[31;43m\x1b[4mthere\x1b[0m")
+#     logger.print("\x1b[38;5;196m\x1b[48;5;232m\x1b[4mmr.\x1b[0m logger")
+#     logger.print(
+#         "\x1b[38;2;96;140;240m\x1b[48;2;240;140;10mmrs.\x1b[0m logger"
+#     )
+#     logger.finalize()
+
+#     # Load the HTML file and make sure it checks out.
+#     html_file = logger.stream_dir / f"{logger.name}.html"
+#     assert html_file.exists()
+#     with html_file.open("r") as hf:
+#         html_text = hf.read()
+#     assert "\x1b" not in html_text
+#     assert ">Hello" in html_text
+#     assert ">there" in html_text
+#     assert ">mr. logger" in html_text
+#     assert "color: rgb(255, 0, 0)" in html_text
+#     assert "background-color: rgb(" in html_text
+#     assert ">mrs. logger" in html_text
+#     assert "color: rgb(96, 140, 240)" in html_text
+#     assert "background-color: rgb(240, 140, 10)" in html_text
+
+
+# def test_html_print(capsys: CaptureFixture) -> None:
+#     """
+#     Ensure :func:`html_print` doesn't print to the console.
+
+#     Ensure the :func:`print` method prints to both the HTML log and the
+#     console, but the :func:`html_print` method only prints to the log
+#     file.
+
+#     Parameters:
+#         capsys:  A fixture for capturing the ``stdout``.
+#     """
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     logger.html_print(
+#         "The quick brown fox jumps over the lazy dog.", msg_title="Brown Fox"
+#     )
+#     logger.print("The quick orange zebra jumps over the lazy dog.")
+#     out, err = capsys.readouterr()
+#     logger.finalize()
+
+#     # Load the HTML file and make sure it checks out.
+#     html_file = logger.stream_dir / f"{logger.name}.html"
+#     assert html_file.exists()
+#     with html_file.open("r") as hf:
+#         html_text = hf.read()
+#     assert "brown fox" not in out
+#     assert "brown fox" not in err
+#     assert "Brown Fox" not in out
+#     assert "Brown Fox" not in err
+#     assert "brown fox" in html_text
+#     assert "Brown Fox" in html_text
+#     assert "orange zebra" not in err
+#     assert "orange zebra" in out
+#     assert "orange zebra" in html_text
+
+
+# def test_append_mode() -> None:
+#     """Ensure we're able to append to a previously generated log file."""
+#     logger1 = ShellLogger(stack()[0][3] + "_1", log_dir=Path.cwd())
+#     logger1.log("Print HELLO to stdout", "echo HELLO")
+#     logger1.print("Printed once to stdout")
+#     logger1.html_print("Printed ONCE to STDOUT")
+#     logger1.finalize()
+
+#     logger2 = ShellLogger.append(logger1.html_file)
+#     logger2.log("Print THERE to stdout", "echo THERE")
+#     logger2.print("Printed twice to stdout")
+#     logger2.html_print("Printed TWICE to STDOUT")
+#     logger2.finalize()
+
+#     logger3 = ShellLogger.append(logger2.log_dir)
+#     logger3.log("Print LOGGER to stdout", "echo LOGGER")
+#     logger3.print("Printed thrice to stdout")
+#     logger3.html_print("Printed THRICE to STDOUT")
+#     logger3.finalize()
+
+#     logger4 = ShellLogger.append(logger3.stream_dir)
+#     logger4.log("Print !!! to stdout", "echo '!!!'")
+#     logger4.print("Printed finally to stdout")
+#     logger4.html_print("Printed FINALLY to STDOUT")
+#     logger4.finalize()
+
+#     logger5 = ShellLogger.append(logger4.stream_dir / f"{logger4.name}.json")
+#     logger5.log("Print 111 to stdout", "echo '111'")
+#     logger5.print("Printed for real to stdout")
+#     logger5.html_print("Printed FOR REAL to STDOUT")
+#     logger5.finalize()
+
+#     # Load the HTML file and ensure it checks out.
+#     html_file = logger1.stream_dir / f"{logger1.name}.html"
+#     assert html_file.exists()
+#     with html_file.open("r") as hf:
+#         html_text = hf.read()
+#     assert "once" in html_text
+#     assert "ONCE" in html_text
+#     assert "HELLO" in html_text
+#     assert "twice" in html_text
+#     assert "TWICE" in html_text
+#     assert "THERE" in html_text
+#     assert "thrice" in html_text
+#     assert "THRICE" in html_text
+#     assert "LOGGER" in html_text
+#     assert "finally" in html_text
+#     assert "FINALLY" in html_text
+#     assert "!!!" in html_text
+#     assert "for real" in html_text
+#     assert "FOR REAL" in html_text
+#     assert "111" in html_text
+
+
+# def test_invalid_decodings() -> None:
+#     """Ensure we appropriately handle invalid bytes when decoding output."""
+#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+#     result = logger.log(
+#         "Print invalid start byte for bytes decode()",
+#         "printf '\\xFDHello\\n'",
+#         return_info=True,
+#     )
+#     assert result["stdout"] == "Hello\n"

From f33ac4f9a86f73d9d036911f0811a2a44a050232 Mon Sep 17 00:00:00 2001
From: "Jason M. Gates" 
Date: Wed, 10 Jul 2024 08:53:00 -0600
Subject: [PATCH 3/6] test: Add dummy test to show pytest works

---
 test/test_shell_logger.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/test/test_shell_logger.py b/test/test_shell_logger.py
index cd4f600..69b6206 100644
--- a/test/test_shell_logger.py
+++ b/test/test_shell_logger.py
@@ -25,6 +25,10 @@
     psutil = None
 
 
+def test_hello_world() -> None:
+    print("Hello World")
+
+
 # @pytest.fixture(autouse=True)
 # def _use_tmpdir(monkeypatch: MonkeyPatch, tmpdir: Path) -> None:
 #     """

From f777fafa8c8767f0275fc319b9ffc754f67ab365 Mon Sep 17 00:00:00 2001
From: "Jason M. Gates" 
Date: Wed, 10 Jul 2024 09:15:30 -0600
Subject: [PATCH 4/6] test: Enable one test

---
 test/test_shell_logger.py | 22 +++++++++++-----------
 1 file changed, 11 insertions(+), 11 deletions(-)

diff --git a/test/test_shell_logger.py b/test/test_shell_logger.py
index 69b6206..12c55a5 100644
--- a/test/test_shell_logger.py
+++ b/test/test_shell_logger.py
@@ -90,17 +90,17 @@ def test_hello_world() -> None:
 #     return parent
 
 
-# def test_initialization_creates_stream_dir() -> None:
-#     """
-#     Ensure the stream directory is created.
-
-#     Verify the initialization of a parent :class:`ShellLogger` object
-#     creates a temporary directory
-#     (``log_dir/%Y-%m-%d_%H%M%S``) if not already created.
-#     """
-#     logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
-#     timestamp = logger.init_time.strftime("%Y-%m-%d_%H.%M.%S.%f")
-#     assert len(list(Path.cwd().glob(f"{timestamp}_*"))) == 1
+def test_initialization_creates_stream_dir() -> None:
+    """
+    Ensure the stream directory is created.
+
+    Verify the initialization of a parent :class:`ShellLogger` object
+    creates a temporary directory
+    (``log_dir/%Y-%m-%d_%H%M%S``) if not already created.
+    """
+    logger = ShellLogger(stack()[0][3], log_dir=Path.cwd())
+    timestamp = logger.init_time.strftime("%Y-%m-%d_%H.%M.%S.%f")
+    assert len(list(Path.cwd().glob(f"{timestamp}_*"))) == 1
 
 
 # def test_initialization_creates_html_file() -> None:

From 1de571a2b0a0a804428c126f1d0bb92090ea3511 Mon Sep 17 00:00:00 2001
From: "Jason M. Gates" 
Date: Wed, 10 Jul 2024 09:00:06 -0600
Subject: [PATCH 5/6] chore: Snoop on the constructor

---
 .github/workflows/continuous-integration.yml | 1 +
 shell_logger/shell.py                        | 7 +++++++
 shell_logger/shell_logger.py                 | 3 +++
 3 files changed, 11 insertions(+)

diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 6efaf32..4b553e6 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -35,6 +35,7 @@ jobs:
             -r doc/requirements.txt \
             -r example/requirements.txt \
             -r test/requirements.txt
+          python3 -m pip install snoop
 
       - name: Test install
         run: python3 -m pip install .
diff --git a/shell_logger/shell.py b/shell_logger/shell.py
index de47d3c..3ce9a3b 100644
--- a/shell_logger/shell.py
+++ b/shell_logger/shell.py
@@ -20,6 +20,8 @@
 from types import SimpleNamespace
 from typing import IO, List, Optional, TextIO, Tuple
 
+import snoop
+
 
 END_OF_READ = 4
 
@@ -44,6 +46,7 @@ class Shell:
             with the shell.
     """
 
+    @snoop
     def __init__(
         self, pwd: Optional[Path] = None, *, login_shell: bool = False
     ) -> None:
@@ -145,6 +148,7 @@ def pwd(self) -> str:
         directory, _ = self.auxiliary_command(posix="pwd", strip=True)
         return directory
 
+    @snoop
     def cd(self, path: Path) -> None:
         """
         Change to the given directory.
@@ -318,6 +322,7 @@ def write(input_file: TextIO, output_files: List[TextIO]) -> None:
                 file.close()
         return SimpleNamespace(stdout_str=stdout_str, stderr_str=stderr_str)
 
+    @snoop
     def auxiliary_command(
         self, **kwargs
     ) -> Tuple[Optional[str], Optional[str]]:
@@ -351,6 +356,8 @@ def auxiliary_command(
             stderr = ""
 
             max_anonymous_pipe_buffer_size = 65536
+
+            # This next line is where the hang occurs.
             aux = os.read(self.aux_stdout_rfd, max_anonymous_pipe_buffer_size)
             while aux[-1] != END_OF_READ:
                 stdout += aux.decode()
diff --git a/shell_logger/shell_logger.py b/shell_logger/shell_logger.py
index 25a1278..6f92b90 100644
--- a/shell_logger/shell_logger.py
+++ b/shell_logger/shell_logger.py
@@ -21,6 +21,8 @@
 from types import SimpleNamespace
 from typing import Iterator, List, Optional, Union
 
+import snoop
+
 from .html_utilities import (
     append_html,
     child_logger_card,
@@ -130,6 +132,7 @@ def append(path: Path) -> ShellLogger:
         with path.open("r") as jf:
             return json.load(jf, cls=ShellLoggerDecoder)
 
+    @snoop
     def __init__(  # noqa: PLR0913
         self,
         name: str,

From d9dd0c0ae7802996588946fc9f50d3d6399d923d Mon Sep 17 00:00:00 2001
From: "Jason M. Gates" 
Date: Wed, 10 Jul 2024 09:19:57 -0600
Subject: [PATCH 6/6] ci: Don't have pytest capture output

---
 .github/workflows/continuous-integration.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml
index 4b553e6..250e0dc 100644
--- a/.github/workflows/continuous-integration.yml
+++ b/.github/workflows/continuous-integration.yml
@@ -45,7 +45,7 @@ jobs:
         with:
           timeout_minutes: 10
           max_attempts: 3
-          command: python3 -m pytest --verbose --cov=shell_logger test/
+          command: python3 -m pytest -s --verbose --cov=shell_logger test/
 
       - name: Upload coverage reports to Codecov
         uses: codecov/codecov-action@v3