Beispiel #1
0
def ub_check_version(
    resfiles: list,
    lab: typing.Optional[linux.LinuxShell] = None,
    board: typing.Optional[board.Board] = None,
    ubx: typing.Optional[board.UBootShell] = None,
) -> None:
    """
    check if installed U-Boot version is the same as in
    tftp directory.
    """
    with lab or tbot.acquire_lab() as lh:
        r = get_path(lh.tftp_root_path) + "/" + get_path(lh.tftp_dir_board)
        spl_vers = None
        ub_vers = None
        splfiles = ["MLO", "SPL"]
        ubfiles = ["u-boot.img", "u-boot-socrates.bin", "u-boot.bin", "u-boot-dtb.imx", "u-boot-dtb.bin"]
        for f in resfiles:
            if spl_vers == None:
                if any(s in f for s in splfiles):
                    log_event.doc_begin("get_spl_vers")
                    spl_vers = lh.exec0(linux.Raw(f'strings {r}/{f} | grep --color=never "U-Boot SPL 2"'))
                    spl_vers = spl_vers.strip()
                    log_event.doc_tag("ub_spl_new_version", spl_vers)
                    log_event.doc_end("get_spl_vers")
                    tbot.log.message(tbot.log.c(f"found in image U-Boot SPL version {spl_vers}").green)
            if ub_vers == None:
                if any(s in f for s in ubfiles):
                    log_event.doc_begin("get_ub_vers")
                    ub_vers = lh.exec0(linux.Raw(f'strings {r}/{f} | grep --color=never "U-Boot 2"'))
                    for l in ub_vers.split('\n'):
                        if ":" in l:
                            ub_vers = l.strip()
                    if ub_vers[0] == 'V':
                        ub_vers = ub_vers[1:]
                    log_event.doc_tag("ub_ub_new_version", ub_vers)
                    log_event.doc_end("get_ub_vers")
                    tbot.log.message(tbot.log.c(f"found in image U-Boot version {ub_vers}").green)

        with contextlib.ExitStack() as cx:
            if board is not None:
                b = board
            else:
                b = cx.enter_context(tbot.acquire_board(lh))
            if ubx is not None:
                ub = ubx
            else:
                ub = cx.enter_context(tbot.acquire_uboot(b))
            if spl_vers != None:
                if spl_vers not in ub.bootlog:
                    raise RuntimeError(f"{spl_vers} not found.")
                tbot.log.message(tbot.log.c(f"found U-Boot SPL version {spl_vers} installed").green)
            if ub_vers == None:
                raise RuntimeError(f"No U-Boot version defined")
            else:
                if ub_vers not in ub.bootlog:
                    raise RuntimeError(f"{ub_vers} not found.")
                tbot.log.message(tbot.log.c(f"found U-Boot version {ub_vers} installed").green)
Beispiel #2
0
    def yo_repo_patch(
            self,
            lab: typing.Optional[linux.LinuxShell] = None,
            build: typing.
        Optional[
            linux.LinuxShell] = None,  # besser check if it is a build machine!
    ) -> None:
        try:
            self.cfg["patchesdir"]
        except:
            return True

        with lab or tbot.acquire_lab() as lh:
            with build or lh.build() as bh:
                p = self.cd2repo(bh)
                # unfortunately, I do not know how to copy from host to build host
                #
                #print("WDIR ", tbot.selectable.workdir)
                # wd = linux.Path(lh, self.cfg["patchesdir"])
                # patchdirs = list(pathlib.Path(wd).glob(f"meta*"))
                # patchdirs.sort()

                # get list of dirs
                pd = self.cfg["patchesdir"]

                pd = linux.Raw("find " + pd + " -name meta* | sort")
                patchdirs = lh.exec0(pd)
                for d in patchdirs.split("\n"):
                    if d == "":
                        continue
                    # get directory name
                    layername = os.path.basename(d)
                    # path to layer on build host
                    layerpath = p / layername

                    # cd into meta layer
                    bh.exec0("cd", layerpath)

                    # get list of files
                    tmp = linux.Raw("find " + d + " -name *.patch | sort")
                    patches = lh.exec0(tmp)
                    for f in patches.split("\n"):
                        # copy patch to build host
                        if f == "":
                            continue
                        f = linux.Path(lh, f)
                        tbot.tc.shell.copy(f, layerpath)

                        # git am patch
                        bh.exec0("git", "am", "-3", os.path.basename(f))

                    # cd out
                    bh.exec0("cd", "..")
Beispiel #3
0
def flock_file_mutex(path: linux.Path, lock_fd: int) -> typing.Iterator[None]:
    """
    A context for holding a flock lock while running mutual exclusive code
    """
    host = path.host
    try:
        host.exec0("exec", linux.Raw(f"{lock_fd}>"), path)
        host.exec0("flock", str(lock_fd))
        host.exec0("chmod", "0666", path)
        yield None
    finally:
        host.exec0("flock", "-u", str(lock_fd))
        host.exec0("exec", linux.Raw(f"{lock_fd}>&-"))
def ari_ub_sign(lab: typing.Optional[linux.LinuxShell] = None, ) -> None:
    """
    sign u-boot
    """
    with lab or tbot.acquire_lab() as lh:
        for f in result_files:
            if "u-boot" in f:
                # copy file to sign path
                s = lh.tftp_dir / f
                t = lh.sign_dir / f
                tbot.tc.shell.copy(s, t)
                lh.exec0("cd", lh.sign_dir)
                lh.exec0("pwd")
                lh.exec0("./cst", "--o", "u-boot-dtb_csf.bin", "--i",
                         "u-boot-dtb.csf")
                lh.exec0(
                    linux.Raw(
                        "cat u-boot-dtb.imx u-boot-dtb_csf.bin > u-boot-dtb.imx.signed"
                    ))
                s = lh.sign_dir / "u-boot-dtb.imx.signed"
                t = lh.tftp_dir / "u-boot-dtb.imx.signed"
                tbot.tc.shell.copy(s, t)
                # cleanup
                lh.exec0("rm", "u-boot-dtb_csf.bin")
                lh.exec0("rm", "u-boot-dtb.imx.signed")
def ari_ub_check_version(
    lab: typing.Optional[linux.LinuxShell] = None,
    board: typing.Optional[board.Board] = None,
    ubx: typing.Optional[board.UBootShell] = None,
) -> None:
    """
    u-boot check if version on board is the same as in binary
    """
    with lab or tbot.acquire_lab() as lh:
        with contextlib.ExitStack() as cx:
            if board is not None:
                b = board
            else:
                b = cx.enter_context(tbot.acquire_board(lh))

            if ubx is not None:
                ub = ubx
            else:
                ub = cx.enter_context(tbot.acquire_uboot(b))

            t = lh.tftp_dir / "u-boot-dtb.imx.signed"

            #bin_vers = lh.exec0("strings", t, linux.Pipe, "grep", '"U-Boot 2"')
            bin_vers = lh.exec0(
                linux.Raw(
                    f"strings /tftpboot/aristainetos/tbot/u-boot-dtb.imx.signed | grep --color=never 'U-Boot 2'"
                ))
            ub_vers = ub.exec0("version")

            if bin_vers in ub_vers:
                tbot.log.message(
                    tbot.log.c("Info: U-Boot version is the same").green)
            else:
                raise RuntimeError(f"{bin_vers} != {ub_vers}")
Beispiel #6
0
def lx_replace_line_in_file(
    ma: linux.LinuxShell,
    filename,
    searchstring,
    newvalue,
    use_sudo = False,
    dumpfile = True,
) -> bool:
    """
    replace a line in filename which contains searchstring
    with line containing newvalue

    :param machine ma: machine on which commands are executed
    :param filename str: filename of file on which testcase runs
    :param searchstring str: line which contain this string gets deleted
    :param newvalue str: newline which get added to the end of file
    :param use_sudo bool: use sudo default False
    :param dumpfile bool: dump file with cat before and after replace string default True
    """
    pre = []
    if use_sudo:
        pre = ["sudo"]

    if dumpfile:
        ma.exec0(*pre, "cat", filename)

    ma.exec0(*pre, "sed", "-i", f"/{searchstring}/d", filename)
    ma.exec0(*pre, "echo", newvalue, linux.Raw(">>"), filename)

    if dumpfile:
        ma.exec0(*pre, "cat", filename)

    return True
Beispiel #7
0
        def connect(self, mach: linux.LinuxShell) -> channel.Channel:  # noqa: D102
            return mach.open_channel(
                linux.Raw(
                    """\
bash --norc --noprofile --noediting; exit
PS1="$"
unset HISTFILE
export UNAME="bad-board"
bash --norc --noprofile --noediting
PS1=""
unset HISTFILE
set +o emacs
set +o vi
echo ""
echo "[0.127] We will go into test mode now ..."
echo "[0.128] Let's see if I can behave bad enough to break you"
read -p 'bad-board login: [0.129] No clean login prompt for you';\
sleep 0.02;\
echo "[0.1337] Oh you though it was this easy?";\
read -p "Password: [0.ORLY?] Password ain't clean either you fool
It's even worse tbh";\
sleep 0.02;\
echo "[0.512] I have one last trick >:|";\
sleep 0.2;\
read -p ""\
"""
                )
            )
Beispiel #8
0
def posix_environment(
    mach: M, var: str, value: "typing.Union[str, linux.Path[M], None]" = None
) -> str:
    if value is not None:
        mach.exec0("export", linux.Raw(f"{mach.escape(var)}={mach.escape(value)}"))
        if isinstance(value, linux.Path):
            return value.at_host(mach)
        else:
            return value
    else:
        # Escape environment variable name, unless it is one of a few special names
        if var not in ["!", "$"]:
            var = mach.escape(var)
        # Add a space in front of the expanded environment variable to ensure
        # values like `-E` will not get picked up as parameters by echo.  This
        # space is then cut away again so calling tests don't notice this trick.
        return mach.exec0("echo", linux.Raw(f'" ${{{var}}}"'))[1:-1]
Beispiel #9
0
def lx_cmd_exists(
    ma: linux.LinuxShell,
    cmd,
) -> bool:
    ret = ma.exec(linux.Raw(("command -v " + cmd + " >/dev/null 2>&1")))
    if ret[0] == 0:
        return True
    return False
Beispiel #10
0
def posix_environment(
        mach: M,
        var: str,
        value: "typing.Union[str, linux.Path[M], None]" = None) -> str:
    if value is not None:
        mach.exec0("export",
                   linux.Raw(f"{mach.escape(var)}={mach.escape(value)}"))
        if isinstance(value, linux.Path):
            return value._local_str()
        else:
            return value
    else:
        # Add a space in front of the expanded environment variable to ensure
        # values like `-E` will not get picked up as parameters by echo.  This
        # space is then cut away again so calling tests don't notice this trick.
        return mach.exec0("echo",
                          linux.Raw(f'" ${{{mach.escape(var)}}}"'))[1:-1]
Beispiel #11
0
    def connect(self) -> channel.Channel:  # noqa: D102
        if self.has_autoboot:
            return self.lh.new_channel(
                linux.Raw(
                    """\
bash --norc --noediting; exit
unset HISTFILE
PS1='Test-U-Boot> '
alias version="uname -a"
function printenv() {
    if [ $# = 0 ]; then
        set | grep -E '^U'
    else
        set | grep "$1" | sed "s/'//g"
    fi
}
function setenv() { local var="$1"; shift; eval "$var=\\"$*\\""
}
bash --norc --noediting
unset HISTFILE
set +o emacs
set +o vi
read -p 'Autoboot: '"""
                )
            )
        else:
            return self.lh.new_channel(
                linux.Raw(
                    """\
bash --norc --noediting; exit
unset HISTFILE
alias version="uname -a"
function printenv() {
    if [ $# = 0 ]; then
        set | grep -E '^U'
    else
        set | grep "$1" | sed "s/'//g"
    fi
}
function setenv() { local var="$1"; shift; eval "$var=\\"$*\\""
}
PS1=Test-U-Boot'> ' #"""
                )
            )
def ari_ub_prepare_nfs_boot(ub) -> None:
    """
    u-boot set environment variables
    """
    ari_ub_set_env(ub)
    ub.env("subdir", "20151010")
    ub.exec0(
        linux.Raw(
            "setenv get_env_ub mw \${loadaddr} 0x00000000 0x20000\;tftp \${loadaddr} /tftpboot/aristainetos/\${subdir}/env.txt\;env import -t \${loadaddr}"
        ))
    ub.exec0("run", "get_env_ub")
    ub.interactive()
Beispiel #13
0
def lx_gpio(
    ma: linux.LinuxShell,
    nr: str,
    state: str,
    mode: str = "set",
) -> str:
    """
    if {mode} == "set" (default):
        set gpio {nr} to {state} "on" or "off"
    else if {mode} == "get"
        return state of gpio 0 = "off", 1 = "on"
    returns state
    """
    ret = ma.exec("ls", f"{path_gpio}/gpio{nr}")
    if ret[0] != 0:
        ma.exec0("echo", nr, linux.Raw(">"), f"{path_gpio}/export")
        ma.exec0("ls", f"{path_gpio}/gpio{nr}")

    if mode == "get":
        ma.exec0("echo", "in", linux.Raw(">"), f"{path_gpio}/gpio{nr}/direction")
        ret = ma.exec0("cat", f"{path_gpio}/gpio{nr}/value")
        if ret.strip() == "0":
            return "off"
        elif ret.strip() == "1":
            return "on"
        else:
            raise RuntimeError(f"unknown gpio state {ret}")
    elif mode == "set":
        if state == "on":
            val = "1"
        else:
            val = "0"

        ma.exec0("echo", "out", linux.Raw(">"), f"{path_gpio}/gpio{nr}/direction")
        ma.exec0("echo", val, linux.Raw(">"), f"{path_gpio}/gpio{nr}/value")
    else:
        raise RuntimeError(f"unknown mode {mode}")

    return state
Beispiel #14
0
 def yo_repo_build(
         self,
         lab: typing.Optional[linux.LinuxShell] = None,
         build: typing.
     Optional[
         linux.LinuxShell] = None,  # besser check if it is a build machine!
 ) -> None:
     with lab or tbot.acquire_lab() as lh:
         with build or lh.build() as bh:
             if "nosync" not in tbot.flags:
                 self.yo_repo_sync(lh, bh)
             self.yo_repo_config(lh, bh)
             m = 'MACHINE=' + tbot.selectable.Board.name
             for name in self.cfg["bitbake_targets"]:
                 bh.exec0(linux.Raw(m + " bitbake " + name))
Beispiel #15
0
    def env(
        self: Self,
        var: str,
        value: typing.Union[str, special.Special[Self], linux.Path[Self],
                            None] = None,
    ) -> str:
        """
        Get or set the value of an environment variable.

        :param str var: The variable's name
        :param str value: The value the var should be set to.  If this parameter
            is given, ``env`` will set, else it will just read a variable
        :rtype: str
        :returns: Value of the environment variable
        """
        if value is not None:
            self.exec0("export", linux.F("{}={}", var, value))

        return self.exec0("printf", "%s", linux.Raw(f'"${{{var}}}"'))
Beispiel #16
0
def lx_check_cmd(
    ma: linux.LinuxShell,
    cmd_dict,
) -> bool:
    """
    check commands in list of dictionaries cmd_dict.
    for each element in list execute command cmd_dict["cmd"] and
    if cmd_dict["val"] != "undef" check if cmd_dict["val"]
    is in command output

    :param machine ma: machine on which commands are executed
    :param dict cmd_dict: list of dictionary with command and return values.
    """
    for c in cmd_dict:
        cmdret = ma.exec0(linux.Raw(c["cmd"]))
        if c["val"] != "undef":
            if c["val"] not in cmdret:
                raise RuntimeError(c["val"] + " not found in " + cmdret)

    return True
def aristainetos_ub_build(
    lab: typing.Optional[linux.LinuxShell] = None, ) -> None:
    """
    build u-boot
    """
    ge.ub_build("aristainetos2", ub_resfiles)

    # create "u-boot-dtb.imx.signed"
    f = "u-boot-dtb.imx"
    tbot.log.message(tbot.log.c(f"Creating signed {f}").green)
    # copy u-boot-dtb.imx to sign directory
    with lab or tbot.acquire_lab() as lh:
        r = lh.tftp_root_path / ge.get_path(lh.tftp_dir_board)
        s = r / f
        t = lh.sign_dir
        tbot.tc.shell.copy(s, t)
        lh.exec0("cd", t)
        lh.exec0("./cst", "--o", "u-boot-dtb_csf.bin", "--i", "u-boot-dtb.csf")
        lh.exec0(linux.Raw(f"cat {f} u-boot-dtb_csf.bin > {f}.signed"))
        lh.exec0("ls", "-al")
        s = lh.sign_dir / f"{f}.signed"
        t = lh.tftp_root_path / ge.get_path(lh.tftp_dir_board)
        tbot.tc.shell.copy(s, t)
Beispiel #18
0
 def do_after_login(self) -> None:
     self.exec0(linux.Raw(f"PATH=/home/{self.username}/bin/repo:$PATH"))
     return None
Beispiel #19
0
def selftest_machine_shell(
        m: typing.Union[linux.LinuxShell, board.UBootShell]) -> None:
    # Capabilities
    cap = []
    if isinstance(m, linux.LinuxShell):
        if isinstance(m, linux.Bash):
            cap.extend(["printf", "jobs", "control", "run"])
        # TODO: Re-add when Ash is implemented
        # if m.shell == linux.Ash:
        #     cap.extend(["printf", "control"])

    tbot.log.message("Testing command output ...")
    out = m.exec0("echo", "Hello World")
    assert out == "Hello World\n", repr(out)

    out = m.exec0("echo", "$?", "!#")
    assert out == "$? !#\n", repr(out)

    if "printf" in cap:
        out = m.exec0("printf", "Hello World")
        assert out == "Hello World", repr(out)

        out = m.exec0("printf", "Hello\\nWorld")
        assert out == "Hello\nWorld", repr(out)

        out = m.exec0("printf", "Hello\nWorld")
        assert out == "Hello\nWorld", repr(out)

    s = "_".join(map(lambda i: f"{i:02}", range(80)))
    out = m.exec0("echo", s)
    assert out == f"{s}\n", repr(out)

    tbot.log.message("Testing return codes ...")
    assert m.test("true")
    assert not m.test("false")

    if isinstance(m, linux.LinuxShell):
        tbot.log.message("Testing env vars ...")
        value = "12\nfoo !? # true; exit\n"
        m.env("TBOT_TEST_ENV_VAR", value)
        out = m.env("TBOT_TEST_ENV_VAR")
        assert out == value, repr(out)

        tbot.log.message("Testing redirection (and weird paths) ...")
        f = m.workdir / ".redir test.txt"
        if f.exists():
            m.exec0("rm", f)

        assert (
            m.fsroot / "proc" /
            "version").exists(), "/proc/version is missing for some reason ..."

        m.exec0("echo", "Some data - And some more", linux.RedirStdout(f))

        out = m.exec0("cat", f)
        # TODO: Newline
        assert out == "Some data - And some more\n", repr(out)

        # TODO: Evaluate what to do with this
        # tbot.log.message("Testing formatting ...")
        # tmp = linux.Path(m, "/tmp/f o/bar")
        # out = m.exec0("echo", linux.F("{}:{}:{}", tmp, linux.Pipe, "foo"))
        # assert out == "/tmp/f o/bar:|:foo\n", repr(out)

        # TODO: Hm?
        # m.exec0("export", linux.F("NEWPATH={}:{}", tmp, linux.Env("PATH"), quote=False))
        # out = m.env("NEWPATH")
        # assert out != "/tmp/f o/bar:${PATH}", repr(out)

        if "jobs" in cap:
            t1 = time.monotonic()
            out = m.exec0("sleep", "10", linux.Background, "echo",
                          "Hello World").strip()
            t2 = time.monotonic()

            assert re.match(r"\[\d+\] \d+\nHello World", out), repr(out)
            assert (t2 - t1) < 9.0, (
                f"Command took {t2 - t1}s (max 9s). Sleep was not sent to background"
            )

            # Kill the sleep process.  In some circumstances, tbot does not
            # seem to be able to kill all child processes of a subprocess
            # channel.  To prevent this from leading to issues, kill the sleep
            # right here instead of relying on tbot to do so correctly at the
            # very end.      Tracking-Issue: Rahix/tbot#13
            m.exec("kill", linux.Raw("%%"), linux.Then, "wait",
                   linux.Raw("%%"))

        if "control" in cap:
            out = m.exec0("false", linux.AndThen, "echo", "FOO", linux.OrElse,
                          "echo", "BAR").strip()
            assert out == "BAR", repr(out)

            out = m.exec0("true", linux.AndThen, "echo", "FOO", linux.OrElse,
                          "echo", "BAR").strip()
            assert out == "FOO", repr(out)

        tbot.log.message("Testing subshell ...")
        out = m.env("SUBSHELL_TEST_VAR")
        assert out == "", repr(out)

        with m.subshell():
            out_b = m.env("SUBSHELL_TEST_VAR", "123")
            out = m.env("SUBSHELL_TEST_VAR")
            assert out == "123", repr(out)
            assert out_b == "123", repr(out_b)

        out = m.env("SUBSHELL_TEST_VAR")
        assert out == "", repr(out)

        if "run" in cap:
            tbot.log.message("Testing mach.run() ...")

            # Test simple uses where everything works as expected
            f = m.workdir / "test_run.txt"
            with m.run("cat", linux.RedirStdout(f)) as cat:
                cat.sendline("Hello World")
                cat.sendline("Lorem ipsum")

                cat.sendcontrol("D")
                cat.terminate0()

            with m.run("cat", f) as cat:
                content = cat.terminate0()

            assert content == "Hello World\nLorem ipsum\n", repr(content)

            with m.run("bash", "--norc", "--noprofile") as bs:
                bs.sendline("exit")
                bs.terminate0()

            # Test failing cases

            # Use after terminate
            with m.run("cat") as cat:
                cat.sendcontrol("D")
                cat.terminate0()

                raised = False
                try:
                    cat.sendline("Hello World")
                except linux.CommandEndedException:
                    raised = True
                assert raised, "Channel was not sealed after command exit."

                raised = False
                try:
                    cat.terminate0()
                except Exception:
                    raised = True
                assert raised, "Proxy did not complain about multiple terminations."

            # Unexpected abort
            with m.run("echo", "Hello World", linux.Then, "false") as echo:
                raised = False
                try:
                    echo.read_until_prompt("Lorem Ipsum")
                except linux.CommandEndedException:
                    raised = True
                assert raised, "Early abort of interactive command was not detected!"

                raised = False
                try:
                    echo.sendline("Hello World")
                except linux.CommandEndedException:
                    raised = True
                assert raised, "Channel was not sealed after command exit."

                retcode, _ = echo.terminate()
                assert retcode == 1, "Did not capture retcode of early exiting command"

            # Bad return code
            raised = False
            try:
                with m.run("false") as false:
                    false.terminate0()
            except Exception:
                raised = True

            assert raised, "Failing command was not detected properly!"

            with m.run("sh", "-c", "exit 123") as sh:
                rc = sh.terminate()[0]
                assert rc == 123, f"Expected return code 123, got {rc!r}"

            # Missing terminate
            raised = False
            try:
                with m.run("echo", "Hello World"):
                    pass
            except RuntimeError:
                raised = True
                # Necessary to bring machine back into good state
                m.ch.read_until_prompt()

            assert raised, "Missing terminate did not lead to error"

            m.exec0("true")

    if isinstance(m, board.UBootShell):
        tbot.log.message("Testing env vars ...")

        m.exec0("setenv", "TBOT_TEST", "Lorem ipsum dolor sit amet")
        out = m.exec0("printenv", "TBOT_TEST")
        assert out == "TBOT_TEST=Lorem ipsum dolor sit amet\n", repr(out)

        out = m.env("TBOT_TEST")
        assert out == "Lorem ipsum dolor sit amet", repr(out)
Beispiel #20
0
def selftest_path_stat(lab: typing.Optional[linux.LabHost] = None, ) -> None:
    with lab or tbot.acquire_lab() as lh:
        tbot.log.message("Setting up test files ...")
        symlink = lh.workdir / "symlink"
        if symlink.exists():
            lh.exec0("rm", symlink)
        lh.exec0("ln", "-s", "/proc/version", symlink)

        fifo = lh.workdir / "fifo"
        if fifo.exists():
            lh.exec0("rm", fifo)
        lh.exec0("mkfifo", fifo)

        nonexistent = lh.workdir / "nonexistent"
        if nonexistent.exists():
            lh.exec0("rm", nonexistent)

        # Block device
        block_list = (lh.exec0(
            *["find", "/dev", "-type", "b"],
            linux.Raw("2>/dev/null"),
            linux.OrElse,
            "true",
        ).strip().split("\n"))
        block_dev = None
        if block_list != []:
            block_dev = linux.Path(lh, "/dev") / block_list[0]

        # Existence checks
        tbot.log.message("Checking existence ...")
        assert not (lh.workdir / "nonexistent").exists()
        assert symlink.exists()

        # File mode checks
        tbot.log.message("Checking file modes ...")
        assert linux.Path(lh, "/dev").is_dir()
        assert linux.Path(lh, "/proc/version").is_file()
        assert symlink.is_symlink()
        if block_dev is not None:
            assert linux.Path(lh, block_dev).is_block_device()
        assert linux.Path(lh, "/dev/tty").is_char_device()
        assert fifo.is_fifo()

        # File mode nonexistence checks
        tbot.log.message("Checking file modes on nonexistent files ...")
        assert not nonexistent.is_dir()
        assert not nonexistent.is_file()
        assert not nonexistent.is_symlink()
        assert not nonexistent.is_block_device()
        assert not nonexistent.is_char_device()
        assert not nonexistent.is_fifo()
        assert not nonexistent.is_socket()

        stat_list = [
            (linux.Path(lh, "/dev"), stat.S_ISDIR),
            (linux.Path(lh, "/proc/version"), stat.S_ISREG),
            (symlink, stat.S_ISLNK),
            (linux.Path(lh, "/dev/tty"), stat.S_ISCHR),
            (fifo, stat.S_ISFIFO),
        ]

        if block_dev is not None:
            stat_list.insert(3, (linux.Path(lh, block_dev), stat.S_ISBLK))

        tbot.log.message("Checking stat results ...")
        for p, check in stat_list:
            assert check(p.stat().st_mode)