From f4d40efc0502bcf868c6d52b217c00e122ca9e6a Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Thu, 25 Jun 2026 20:12:15 +0200 Subject: [PATCH 1/2] test: Move unit tests below jw/pkg Move the unit test hierarchy to below test/unit/python/jw/pkg. Nesting the subdirectories so deeply might be overly careful, but it may as well be not - maybe in the future installable test packages are going to be generated and need simple ways to install without stepping onto each other's toes. If not, it's easier to cut two directory components out than having to reorganize possibly incoherent paths grown over multiple packages. Signed-off-by: Jan Lindemann --- test/unit/python/{lib => jw}/Makefile | 0 test/unit/python/jw/pkg/Makefile | 4 ++++ test/unit/python/jw/pkg/lib/Makefile | 4 ++++ test/unit/python/{ => jw/pkg}/lib/Uri/Makefile | 2 +- test/unit/python/{ => jw/pkg}/lib/Uri/test.py | 0 5 files changed, 9 insertions(+), 1 deletion(-) rename test/unit/python/{lib => jw}/Makefile (100%) create mode 100644 test/unit/python/jw/pkg/Makefile create mode 100644 test/unit/python/jw/pkg/lib/Makefile rename test/unit/python/{ => jw/pkg}/lib/Uri/Makefile (72%) rename test/unit/python/{ => jw/pkg}/lib/Uri/test.py (100%) diff --git a/test/unit/python/lib/Makefile b/test/unit/python/jw/Makefile similarity index 100% rename from test/unit/python/lib/Makefile rename to test/unit/python/jw/Makefile diff --git a/test/unit/python/jw/pkg/Makefile b/test/unit/python/jw/pkg/Makefile new file mode 100644 index 00000000..8b2498dc --- /dev/null +++ b/test/unit/python/jw/pkg/Makefile @@ -0,0 +1,4 @@ +TOPDIR = ../../../../.. + +include $(TOPDIR)/make/proj.mk +include $(JWBDIR)/make/dirs.mk diff --git a/test/unit/python/jw/pkg/lib/Makefile b/test/unit/python/jw/pkg/lib/Makefile new file mode 100644 index 00000000..f817ac8c --- /dev/null +++ b/test/unit/python/jw/pkg/lib/Makefile @@ -0,0 +1,4 @@ +TOPDIR = ../../../../../.. + +include $(TOPDIR)/make/proj.mk +include $(JWBDIR)/make/dirs.mk diff --git a/test/unit/python/lib/Uri/Makefile b/test/unit/python/jw/pkg/lib/Uri/Makefile similarity index 72% rename from test/unit/python/lib/Uri/Makefile rename to test/unit/python/jw/pkg/lib/Uri/Makefile index a2efda8e..de693650 100644 --- a/test/unit/python/lib/Uri/Makefile +++ b/test/unit/python/jw/pkg/lib/Uri/Makefile @@ -1,4 +1,4 @@ -TOPDIR = ../../../../../ +TOPDIR = ../../../../../../.. include $(TOPDIR)/make/proj.mk include $(JWBDIR)/make/py-run.mk diff --git a/test/unit/python/lib/Uri/test.py b/test/unit/python/jw/pkg/lib/Uri/test.py similarity index 100% rename from test/unit/python/lib/Uri/test.py rename to test/unit/python/jw/pkg/lib/Uri/test.py -- 2.55.0 From 78f57c7547516d6ae9bb57e83e36f5cda43f6c43 Mon Sep 17 00:00:00 2001 From: Jan Lindemann Date: Thu, 25 Jun 2026 19:37:34 +0200 Subject: [PATCH 2/2] jw.pkg.lib.Result: Add unit test Add a unit test for the Result class covering: - stdout/stderr property access with various status values - None output handling and exception behavior - Encoding and strip_output property setters - cmd and wd setters - summary, summarize, and __repr__ behavior - matches_error pattern matching Signed-off-by: Jan Lindemann --- test/unit/python/jw/pkg/lib/Result/Makefile | 8 + test/unit/python/jw/pkg/lib/Result/test.py | 159 ++++++++++++++++++++ 2 files changed, 167 insertions(+) create mode 100644 test/unit/python/jw/pkg/lib/Result/Makefile create mode 100644 test/unit/python/jw/pkg/lib/Result/test.py diff --git a/test/unit/python/jw/pkg/lib/Result/Makefile b/test/unit/python/jw/pkg/lib/Result/Makefile new file mode 100644 index 00000000..3d3138c5 --- /dev/null +++ b/test/unit/python/jw/pkg/lib/Result/Makefile @@ -0,0 +1,8 @@ +TOPDIR = ../../../../../../.. + +include $(TOPDIR)/make/proj.mk +include $(JWBDIR)/make/py-run.mk + +all: + +test: run diff --git a/test/unit/python/jw/pkg/lib/Result/test.py b/test/unit/python/jw/pkg/lib/Result/test.py new file mode 100644 index 00000000..b8f25aa9 --- /dev/null +++ b/test/unit/python/jw/pkg/lib/Result/test.py @@ -0,0 +1,159 @@ +from jw.pkg.lib.Result import Result + +# -- Basic construction -- + +r = Result(stdout=b'hello', stderr=b'world', status=0) +assert r.status == 0 +assert r.stdout == b'hello' +assert r.stderr == b'world' + +# -- stdout property -- + +r = Result(stdout=b'test', status=0) +assert r.stdout == b'test' + +# stdout_or_none returns raw value +r = Result(stdout=b'test', status=0) +assert r.stdout_or_none == b'test' + +# stdout_str_or_none returns decoded string +r = Result(stdout=b'test', status=0) +assert r.stdout_str_or_none == 'test' + +# stdout_str returns decoded string +r = Result(stdout=b'test', status=0) +assert r.stdout_str == 'test' + +# stdout returns b'' when stdout is None and status == 0 +r = Result(stdout=None, status=0) +assert r.stdout == b'' + +# stdout raises when stdout is None and status != 0 +r = Result(stdout=None, status=1) +try: + _ = r.stdout + assert False, 'Should have raised' +except Exception: + pass + +# -- stderr property -- + +r = Result(stderr=b'error', status=1) +assert r.stderr == b'error' + +# stderr_or_none returns raw value +r = Result(stderr=b'err', status=1) +assert r.stderr_or_none == b'err' + +# stderr_str_or_none returns decoded string +r = Result(stderr=b'err', status=1) +assert r.stderr_str_or_none == 'err' + +# stderr_str returns decoded string +r = Result(stderr=b'err', status=1) +assert r.stderr_str == 'err' + +# stderr returns b'' when stderr is None and status != 0 (error case) +r = Result(stderr=None, status=1) +assert r.stderr == b'' + +# stderr raises when stderr is None and status == 0 (success case) +r = Result(stderr=None, status=0) +try: + _ = r.stderr + assert False, 'Should have raised' +except Exception: + pass + +# -- None outputs -- + +r = Result(stdout=None, stderr=None, status=1) +assert r.stdout_or_none is None +assert r.stderr_or_none is None +assert r.stdout_str_or_none is None +assert r.stderr_str_or_none is None + +# -- encoding -- + +r = Result(stdout=b'caf\xc3\xa9', encoding='UTF-8') +assert r.encoding == 'UTF-8' +assert r.stdout_str == 'café' + +r = Result(stdout=b'test', encoding='UTF-8') +r.encoding = 'ASCII' +assert r.encoding == 'ASCII' + +# -- strip_output -- + +r = Result(stdout=b' spaces ', status=0) +assert r.stdout_str_or_none == ' spaces ' + +r = Result(stdout=b' spaces ', status=0, strip_output=True) +assert r.stdout_str_or_none == 'spaces' + +r = Result(stdout=b' lines \n', status=0, strip_output=True) +assert r.stdout_str_or_none == 'lines' + +# -- cmd and wd setters -- + +r = Result(cmd=['echo', 'hello'], wd='/tmp') +assert r.cmd == ['echo', 'hello'] +assert r.wd == '/tmp' + +r.cmd = ['ls', '-la'] +r.wd = '/home' +assert r.cmd == ['ls', '-la'] +assert r.wd == '/home' + +# -- summary -- + +r = Result(stdout=b'output', status=0) +assert 'status 0' in r.summary + +r = Result(stderr=b'error', status=1) +assert 'status 1' in r.summary +assert 'stderr' in r.summary + +# -- matches_error -- + +r = Result(stderr=b'fatal error: missing file', status=128) +assert r.matches_error('fatal error') + +r = Result(stderr=b'fatal error: missing file', status=128) +assert not r.matches_error('success') + +r = Result(stdout=b'ok', stderr=b'', status=0) +assert not r.matches_error('.*') + +r = Result(stdout=b'ok', stderr=None, status=0) +assert not r.matches_error('.*') + +# -- summarize with custom cmd/wd -- + +r = Result(stdout=b'out', status=0, cmd=['echo', 'test'], wd='/tmp') +assert 'echo' in r.summary or 'status 0' in r.summary + +# -- __repr__ -- + +r = Result(stdout=b'repr test', status=0) +s = repr(r) +assert '0' in s # verbose=False uses numeric status +assert 'repr test' in s + +# -- encoding setter -- + +r = Result(stdout=b'test', status=0, encoding='UTF-8') +assert r.encoding == 'UTF-8' +r.encoding = 'ASCII' +assert r.encoding == 'ASCII' + +# -- strip_output setter -- + +r = Result(stdout=b' x ', status=0, strip_output=False) +assert r.stdout_str_or_none == ' x ' +r.strip_output = True +# strip_output is a property setter, the value is stored +# But __decode is called at property access time using current strip_output +assert r.stdout_str_or_none == 'x' + +print('All Result tests passed') -- 2.55.0