From: Trevor Woerner <twoerner@gmail.com>
To: yocto-patches@lists.yoctoproject.org
Subject: [wic][PATCH v4 6/6] tests/unit/test_bb_utils: cover mkdirhier()
Date: Mon, 6 Jul 2026 18:29:04 -0400 [thread overview]
Message-ID: <20260706222904.664863-7-twoerner@gmail.com> (raw)
In-Reply-To: <20260706222904.664863-1-twoerner@gmail.com>
Add unit tests for mkdirhier(), the one function in wic.bb.utils. They
pin the behaviour wic relies on so it cannot regress: it creates missing
directories, accepts a directory that already exists, rejects a path
containing an unexpanded bitbake variable (${...}), and allows a name
with a lone brace or a lone dollar, neither of which is the ${ marker.
Three tests cover the OSError handler that the errno import repairs: a
path whose parent component is a regular file, a target that already
exists as a file rather than a directory, and a directory that appears
concurrently mid-call (a create race), which must be treated as success.
Run against a wic.bb.utils that does not import errno, all three fail
with NameError instead, so they catch that bug directly.
The tests replace the tests/unit/.gitkeep placeholder.
AI-Generated: codex/claude-opus 4.8 (xhigh)
Signed-off-by: Trevor Woerner <twoerner@gmail.com>
---
changes in v4:
- reframe the tests to target mkdirhier's own behaviour: drop the
cases that only re-exercised os.makedirs and the assertions that
pinned implementation details.
- the errno fix moved to its own preceding commit; this is tests
only.
changes in v3:
- switch the tests from tempfile.mkdtemp() to pytest's tmp_path
fixture so each test's scratch directory is cleaned up instead of
leaking under /tmp; no change to what is tested.
changes in v2:
- v1 recorded this bug with an xfail marker in one large commit;
v2 drops the xfail, asserts the correct behaviour directly, and
lands the one-line errno-import fix in this same commit so the
test passes green.
---
tests/unit/.gitkeep | 0
tests/unit/test_bb_utils.py | 81 +++++++++++++++++++++++++++++++++++++
2 files changed, 81 insertions(+)
delete mode 100644 tests/unit/.gitkeep
create mode 100644 tests/unit/test_bb_utils.py
diff --git a/tests/unit/.gitkeep b/tests/unit/.gitkeep
deleted file mode 100644
index e69de29bb2d1..000000000000
diff --git a/tests/unit/test_bb_utils.py b/tests/unit/test_bb_utils.py
new file mode 100644
index 000000000000..0300b8d4a1a0
--- /dev/null
+++ b/tests/unit/test_bb_utils.py
@@ -0,0 +1,81 @@
+"""
+Unit tests for wic.bb.utils.mkdirhier: a mkdir -p wrapper that rejects
+unexpanded bitbake variables and, on error, tolerates an already-existing
+directory while re-raising every real failure.
+"""
+import sys
+from pathlib import Path
+
+import pytest
+
+_SRC = Path(__file__).resolve().parent.parent.parent / "src"
+if str(_SRC) not in sys.path:
+ sys.path.insert(0, str(_SRC))
+
+from wic.bb.utils import mkdirhier
+
+
+class TestMkdirhier:
+ def test_creates_missing_directories(self, tmp_path):
+ target = tmp_path / "a" / "b" / "c"
+ mkdirhier(str(target))
+ assert target.is_dir()
+
+ def test_existing_directory_is_accepted(self, tmp_path):
+ # Calling it on a directory that already exists is not an error.
+ mkdirhier(str(tmp_path))
+ mkdirhier(str(tmp_path))
+ assert tmp_path.is_dir()
+
+ def test_unexpanded_bitbake_variable_is_rejected(self, tmp_path):
+ target = tmp_path / "${WORKDIR}" / "sub"
+ with pytest.raises(Exception, match="unexpanded bitbake variable"):
+ mkdirhier(str(target))
+ assert not (tmp_path / "${WORKDIR}").exists()
+
+ def test_plain_brace_is_not_treated_as_a_variable(self, tmp_path):
+ # Only the '${' marker trips the guard; a bare brace is a legal
+ # (if unusual) directory name.
+ target = tmp_path / "plain{brace"
+ mkdirhier(str(target))
+ assert target.is_dir()
+
+ def test_dollar_without_brace_is_allowed(self, tmp_path):
+ # The guard keys on the literal '${' marker; a '$' on its own is
+ # not an unexpanded variable and is a legal directory name.
+ target = tmp_path / "price$5"
+ mkdirhier(str(target))
+ assert target.is_dir()
+
+ def test_path_under_a_file_raises(self, tmp_path):
+ # A parent component that is a regular file makes the underlying
+ # mkdir fail; the error must surface rather than be swallowed.
+ afile = tmp_path / "afile"
+ afile.write_text("x")
+ with pytest.raises(OSError):
+ mkdirhier(str(afile / "sub"))
+
+ def test_existing_file_at_target_raises(self, tmp_path):
+ # The target already exists but is a file, not a directory: the
+ # error must propagate rather than be tolerated.
+ afile = tmp_path / "afile"
+ afile.write_text("x")
+ with pytest.raises(OSError):
+ mkdirhier(str(afile))
+
+ def test_concurrent_creation_is_treated_as_success(self, tmp_path, monkeypatch):
+ # If the directory appears while mkdirhier runs (a create race
+ # with another process), that is success, not an error.
+ import errno
+
+ import wic.bb.utils as bb_utils
+
+ target = tmp_path / "made-concurrently"
+
+ def racing_makedirs(path, exist_ok=False):
+ target.mkdir() # someone else wins the race
+ raise OSError(errno.EEXIST, "File exists", str(path))
+
+ monkeypatch.setattr(bb_utils.os, "makedirs", racing_makedirs)
+ mkdirhier(str(target)) # must not raise
+ assert target.is_dir()
--
2.50.0.173.g8b6f19ccfc3a
next prev parent reply other threads:[~2026-07-06 22:29 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-06 22:28 [wic][PATCH v4 0/6] tests: standalone test-suite framework plus the first unit test Trevor Woerner
2026-07-06 22:28 ` [wic][PATCH v4 1/6] tests: add the standalone unit-test suite skeleton Trevor Woerner
2026-07-06 22:29 ` [wic][PATCH v4 2/6] tests: add conftest with a wic-import preflight and session banner Trevor Woerner
2026-07-06 22:29 ` [wic][PATCH v4 3/6] tests: add optional coverage reporting Trevor Woerner
2026-07-06 22:29 ` [wic][PATCH v4 4/6] add ruff linting Trevor Woerner
2026-07-06 22:29 ` [wic][PATCH v4 5/6] bb/utils: import errno so mkdirhier's OSError handler works Trevor Woerner
2026-07-06 22:29 ` Trevor Woerner [this message]
2026-07-08 14:27 ` [yocto-patches] [wic][PATCH v4 0/6] tests: standalone test-suite framework plus the first unit test Paul Barker
2026-07-09 3:50 ` Trevor Woerner
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260706222904.664863-7-twoerner@gmail.com \
--to=twoerner@gmail.com \
--cc=yocto-patches@lists.yoctoproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.