xibus

experimental pure python async D-Bus library
git clone https://git.ce9e.org/xibus.git

commit
3bc253e4d8737c98084defcd0ccb1a54a7da05fd
parent
6992a76818c92416492c34e59d479a3497df14c5
Author
Tobias Bengfort <tobias.bengfort@posteo.de>
Date
2026-02-16 09:14
add tests

Diffstat

A .github/workflows/main.yml 24 ++++++++++++++++++++++++
M pyproject.toml 4 ++++
A tests/test_marshal.py 436 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

3 files changed, 464 insertions, 0 deletions


diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml

@@ -0,0 +1,24 @@
   -1     1 on: [push]
   -1     2 jobs:
   -1     3   test:
   -1     4     runs-on: ubuntu-latest
   -1     5     steps:
   -1     6     - uses: actions/checkout@v6
   -1     7     - uses: actions/setup-python@v6
   -1     8     - run: python3 -m pip install ruff coverage
   -1     9     - run: ruff check xibus tests
   -1    10     - run: dbus-run-session -- python3 -m coverage run -m unittest discover tests
   -1    11     - run: python3 -m coverage report
   -1    12   publish:
   -1    13     needs: [test]
   -1    14     if: startsWith(github.ref, 'refs/tags')
   -1    15     runs-on: ubuntu-latest
   -1    16     permissions:
   -1    17       id-token: write
   -1    18     steps:
   -1    19     - uses: actions/checkout@v6
   -1    20     - uses: actions/setup-python@v6
   -1    21     - run: python3 -m pip install build
   -1    22     - run: python3 -m build
   -1    23     - name: publish
   -1    24       uses: pypa/gh-action-pypi-publish@release/v1

diff --git a/pyproject.toml b/pyproject.toml

@@ -15,3 +15,7 @@ authors = [
   15    15 
   16    16 [project.urls]
   17    17 Homepage = "https://github.com/xi/xibus"
   -1    18 
   -1    19 [tool.coverage.run]
   -1    20 branch = true
   -1    21 include = ["xibus/**"]

diff --git a/tests/test_marshal.py b/tests/test_marshal.py

@@ -0,0 +1,436 @@
   -1     1 import unittest
   -1     2 
   -1     3 from xibus.message import Msg
   -1     4 from xibus.message import MsgFlag
   -1     5 from xibus.message import MsgType
   -1     6 
   -1     7 MESSAGES = [
   -1     8     {
   -1     9         'message': Msg(
   -1    10             MsgType.METHOD_CALL,
   -1    11             1,
   -1    12             destination='org.freedesktop.DBus',
   -1    13             path='/org/freedesktop/DBus',
   -1    14             iface='org.freedesktop.DBus',
   -1    15             member='Hello',
   -1    16             sig='',
   -1    17         ),
   -1    18         'data': bytes.fromhex(
   -1    19             '6c01000100000000010000006d00000001016f00150000002f6f72672f667265'
   -1    20             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1    21             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1    22             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1    23         ),
   -1    24     },
   -1    25     {
   -1    26         'message': Msg(
   -1    27             MsgType.METHOD_CALL,
   -1    28             1,
   -1    29             destination='org.freedesktop.DBus',
   -1    30             path='/org/freedesktop/DBus',
   -1    31             iface='org.freedesktop.DBus',
   -1    32             member='Hello',
   -1    33             sig='as',
   -1    34             body=(['hello', 'world'],),
   -1    35         ),
   -1    36         'data': bytes.fromhex(
   -1    37             '6c0100011a000000010000007800000001016f00150000002f6f72672f667265'
   -1    38             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1    39             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1    40             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1    41             '0801670002617300160000000500000068656c6c6f00000005000000776f726c'
   -1    42             '6400'
   -1    43         ),
   -1    44     },
   -1    45     {
   -1    46         'message': Msg(
   -1    47             MsgType.METHOD_CALL,
   -1    48             1,
   -1    49             destination='org.freedesktop.DBus',
   -1    50             path='/org/freedesktop/DBus',
   -1    51             iface='org.freedesktop.DBus',
   -1    52             member='Hello',
   -1    53             sig='a(uu)',
   -1    54             body=([(1, 1), (2, 2)],),
   -1    55         ),
   -1    56         'data': bytes.fromhex(
   -1    57             '6c01000118000000010000007b00000001016f00150000002f6f72672f667265'
   -1    58             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1    59             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1    60             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1    61             '0801670005612875752900000000000010000000000000000100000001000000'
   -1    62             '0200000002000000'
   -1    63         ),
   -1    64     },
   -1    65     {
   -1    66         'message': Msg(
   -1    67             MsgType.METHOD_CALL,
   -1    68             1,
   -1    69             destination='org.freedesktop.DBus',
   -1    70             path='/org/freedesktop/DBus',
   -1    71             iface='org.freedesktop.DBus',
   -1    72             member='Hello',
   -1    73             sig='a{ss}',
   -1    74             body=({'foo': 'bar', 'bat': 'baz'},),
   -1    75         ),
   -1    76         'data': bytes.fromhex(
   -1    77             '6c01000128000000010000007b00000001016f00150000002f6f72672f667265'
   -1    78             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1    79             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1    80             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1    81             '0801670005617b73737d000000000000200000000000000003000000666f6f00'
   -1    82             '030000006261720003000000626174000300000062617a00'
   -1    83         ),
   -1    84     },
   -1    85     {
   -1    86         'message': Msg(
   -1    87             MsgType.METHOD_CALL,
   -1    88             1,
   -1    89             destination='org.freedesktop.DBus',
   -1    90             path='/org/freedesktop/DBus',
   -1    91             iface='org.freedesktop.DBus',
   -1    92             member='Hello',
   -1    93             sig='a(as(uu(a{ss})))',
   -1    94             body=(
   -1    95                 [
   -1    96                     (['hello', 'there'], (5, 6, ({'five': 'six', 'seven': 'eight'},))),
   -1    97                     (
   -1    98                         ['to', 'the', 'world'],
   -1    99                         (7, 8, ({'seven': 'eight', 'nine': 'ten'},)),
   -1   100                     ),
   -1   101                 ],
   -1   102             ),
   -1   103         ),
   -1   104         'data': bytes.fromhex(
   -1   105             '6c010001c4000000010000008600000001016f00150000002f6f72672f667265'
   -1   106             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   107             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   108             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   109             '08016700106128617328757528617b73737d292929000000bc00000000000000'
   -1   110             '160000000500000068656c6c6f00000005000000746865726500000000000000'
   -1   111             '05000000060000002e0000000000000004000000666976650000000003000000'
   -1   112             '736978000000000005000000736576656e000000050000006569676874000000'
   -1   113             '1a00000002000000746f0000030000007468650005000000776f726c64000000'
   -1   114             '07000000080000002c0000000000000005000000736576656e00000005000000'
   -1   115             '6569676874000000040000006e696e65000000000300000074656e00'
   -1   116         )
   -1   117     },
   -1   118     {
   -1   119         'message': Msg(
   -1   120             MsgType.METHOD_CALL,
   -1   121             1,
   -1   122             destination='org.freedesktop.DBus',
   -1   123             path='/org/freedesktop/DBus',
   -1   124             iface='org.freedesktop.DBus',
   -1   125             member='Hello',
   -1   126             sig='t',
   -1   127             body=(9007199254740988,),
   -1   128         ),
   -1   129         'data': bytes.fromhex(
   -1   130             '6c01000108000000010000007700000001016f00150000002f6f72672f667265'
   -1   131             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   132             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   133             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   134             '0801670001740000fcffffffffff1f00'
   -1   135         ),
   -1   136     },
   -1   137     {
   -1   138         'message': Msg(
   -1   139             MsgType.METHOD_CALL,
   -1   140             1,
   -1   141             destination='org.freedesktop.DBus',
   -1   142             path='/org/freedesktop/DBus',
   -1   143             iface='org.freedesktop.DBus',
   -1   144             member='Hello',
   -1   145             sig='x',
   -1   146             body=(-9007199254740988,),
   -1   147         ),
   -1   148         'data': bytes.fromhex(
   -1   149             '6c01000108000000010000007700000001016f00150000002f6f72672f667265'
   -1   150             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   151             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   152             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   153             '0801670001780000040000000000e0ff'
   -1   154         ),
   -1   155     },
   -1   156     {
   -1   157         'message': Msg(
   -1   158             MsgType.METHOD_CALL,
   -1   159             1,
   -1   160             destination='org.freedesktop.DBus',
   -1   161             path='/org/freedesktop/DBus',
   -1   162             iface='org.freedesktop.DBus',
   -1   163             member='Hello',
   -1   164             sig='bnqiud',
   -1   165             body=(
   -1   166                 True,
   -1   167                 -200,
   -1   168                 150,
   -1   169                 -20000,
   -1   170                 20000,
   -1   171                 9083492084.4444,
   -1   172             ),
   -1   173         ),
   -1   174         'data': bytes.fromhex(
   -1   175             '6c01000118000000010000007c00000001016f00150000002f6f72672f667265'
   -1   176             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   177             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   178             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   179             '0801670006626e7169756400000000000100000038ff9600e0b1ffff204e0000'
   -1   180             '228ea3b758eb0042'
   -1   181         ),
   -1   182     },
   -1   183     {
   -1   184         'message': Msg(
   -1   185             MsgType.METHOD_CALL,
   -1   186             1,
   -1   187             destination='org.freedesktop.DBus',
   -1   188             path='/org/freedesktop/DBus',
   -1   189             iface='org.freedesktop.DBus',
   -1   190             member='Hello',
   -1   191             sig='v',
   -1   192             body=(('s', 'hello world'),),
   -1   193         ),
   -1   194         'data': bytes.fromhex(
   -1   195             '6c01000114000000010000007700000001016f00150000002f6f72672f667265'
   -1   196             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   197             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   198             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   199             '0801670001760000017300000b00000068656c6c6f20776f726c6400'
   -1   200         ),
   -1   201     },
   -1   202     {
   -1   203         'message': Msg(
   -1   204             MsgType.METHOD_CALL,
   -1   205             1,
   -1   206             destination='org.freedesktop.DBus',
   -1   207             path='/org/freedesktop/DBus',
   -1   208             iface='org.freedesktop.DBus',
   -1   209             member='Hello',
   -1   210             sig='v',
   -1   211             body=(('v', ('s', 'hello')),),
   -1   212         ),
   -1   213         'data': bytes.fromhex(
   -1   214             '6c01000112000000010000007700000001016f00150000002f6f72672f667265'
   -1   215             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   216             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   217             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   218             '080167000176000001760001730000000500000068656c6c6f00'
   -1   219         ),
   -1   220     },
   -1   221     {
   -1   222         'message': Msg(
   -1   223             MsgType.METHOD_CALL,
   -1   224             1,
   -1   225             destination='org.freedesktop.DBus',
   -1   226             path='/org/freedesktop/DBus',
   -1   227             iface='org.freedesktop.DBus',
   -1   228             member='Hello',
   -1   229             sig='a{sv}',
   -1   230             body=(
   -1   231                 {
   -1   232                     'variant_key_1': ('s', 'variant_val_1'),
   -1   233                     'variant_key_2': ('s', 'variant_val_2'),
   -1   234                 },
   -1   235             ),
   -1   236         ),
   -1   237         'data': bytes.fromhex(
   -1   238             '6c01000162000000010000007b00000001016f00150000002f6f72672f667265'
   -1   239             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   240             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   241             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   242             '0801670005617b73767d0000000000005a000000000000000d00000076617269'
   -1   243             '616e745f6b65795f31000173000000000d00000076617269616e745f76616c5f'
   -1   244             '31000000000000000d00000076617269616e745f6b65795f3200017300000000'
   -1   245             '0d00000076617269616e745f76616c5f3200'
   -1   246         ),
   -1   247     },
   -1   248     {
   -1   249         'message': Msg(
   -1   250             MsgType.METHOD_CALL,
   -1   251             1,
   -1   252             destination='org.freedesktop.DBus',
   -1   253             path='/org/freedesktop/DBus',
   -1   254             iface='org.freedesktop.DBus',
   -1   255             member='Hello',
   -1   256             sig='v',
   -1   257             body=(('as', ['foo', 'bar']),),
   -1   258         ),
   -1   259         'data': bytes.fromhex(
   -1   260             '6c01000118000000010000007700000001016f00150000002f6f72672f667265'
   -1   261             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   262             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   263             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   264             '0801670001760000026173001000000003000000666f6f000300000062617200'
   -1   265         ),
   -1   266     },
   -1   267     {
   -1   268         'message': Msg(
   -1   269             MsgType.METHOD_CALL,
   -1   270             1,
   -1   271             destination='org.freedesktop.DBus',
   -1   272             path='/org/freedesktop/DBus',
   -1   273             iface='org.freedesktop.DBus',
   -1   274             member='Hello',
   -1   275             sig='vas',
   -1   276             body=(('v', ('s', 'world')), ['bar']),
   -1   277         ),
   -1   278         'data': bytes.fromhex(
   -1   279             '6c01000120000000010000007900000001016f00150000002f6f72672f667265'
   -1   280             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   281             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   282             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   283             '08016700037661730000000000000000017600017300000005000000776f726c'
   -1   284             '64000000080000000300000062617200'
   -1   285         ),
   -1   286     },
   -1   287     {
   -1   288         'message': Msg(
   -1   289             MsgType.METHOD_CALL,
   -1   290             1,
   -1   291             destination='org.freedesktop.DBus',
   -1   292             path='/org/freedesktop/DBus',
   -1   293             iface='org.freedesktop.DBus',
   -1   294             member='Hello',
   -1   295             sig='asbbasbb',
   -1   296             body=(['hello', 'worl'], True, False, ['hello', 'worl'], True, False),
   -1   297         ),
   -1   298         'data': bytes.fromhex(
   -1   299             '6c01000148000000010000007e00000001016f00150000002f6f72672f667265'
   -1   300             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   301             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   302             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   303             '08016700086173626261736262000000150000000500000068656c6c6f000000'
   -1   304             '04000000776f726c000000000100000000000000150000000500000068656c6c'
   -1   305             '6f00000004000000776f726c000000000100000000000000'
   -1   306         ),
   -1   307     },
   -1   308     {
   -1   309         'message': Msg(
   -1   310             MsgType.METHOD_CALL,
   -1   311             1,
   -1   312             destination='org.freedesktop.DBus',
   -1   313             path='/org/freedesktop/DBus',
   -1   314             iface='org.freedesktop.DBus',
   -1   315             member='Hello',
   -1   316             sig='as',
   -1   317             body=(['//doesntmatter/über'],),
   -1   318         ),
   -1   319         'data': bytes.fromhex(
   -1   320             '6c0100011d000000010000007800000001016f00150000002f6f72672f667265'
   -1   321             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   322             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   323             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   324             '080167000261730019000000140000002f2f646f65736e746d61747465722fc3'
   -1   325             'bc62657200'
   -1   326         ),
   -1   327     },
   -1   328     {
   -1   329         'message': Msg(
   -1   330             MsgType.METHOD_CALL,
   -1   331             1,
   -1   332             destination='org.freedesktop.DBus',
   -1   333             path='/org/freedesktop/DBus',
   -1   334             iface='org.freedesktop.DBus',
   -1   335             member='Hello',
   -1   336             sig='an',
   -1   337             body=([-1024],),
   -1   338         ),
   -1   339         'data': bytes.fromhex(
   -1   340             '6c01000106000000010000007800000001016f00150000002f6f72672f667265'
   -1   341             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   342             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   343             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   344             '0801670002616e000200000000fc'
   -1   345         ),
   -1   346     },
   -1   347     {
   -1   348         'message': Msg(
   -1   349             MsgType.SIGNAL,
   -1   350             707226,
   -1   351             flags=MsgFlag.NO_REPLY_EXPECTED,
   -1   352             sender=':1.4',
   -1   353             path='/org/bluez/hci0/dev_D0_C2_4E_08_AB_57',
   -1   354             iface='org.freedesktop.DBus.Properties',
   -1   355             member='PropertiesChanged',
   -1   356             sig='sa{sv}as',
   -1   357             body=(
   -1   358                 'org.bluez.Device1',
   -1   359                 {
   -1   360                     'RSSI': ('n', -86),
   -1   361                     'ManufacturerData': (
   -1   362                         'a{qv}',
   -1   363                         {
   -1   364                             117: (
   -1   365                                 'ay',
   -1   366                                 list(b'B\x04\x01\x01p\xd0\xc2N\x08\xabW\xd2\xc2N\x08\xabV\x01\x00\x00\x00\x00\x00\x00'),
   -1   367                             )
   -1   368                         },
   -1   369                     ),
   -1   370                 },
   -1   371                 [],
   -1   372             ),
   -1   373         ),
   -1   374         'data': bytes.fromhex(
   -1   375             '6c040101780000009aca0a009500000001016f00250000002f6f72672f626c75'
   -1   376             '657a2f686369302f6465765f44305f43325f34455f30385f41425f3537000000'
   -1   377             '020173001f0000006f72672e667265656465736b746f702e444275732e50726f'
   -1   378             '7065727469657300030173001100000050726f706572746965734368616e6765'
   -1   379             '6400000000000000080167000873617b73767d61730000000701730004000000'
   -1   380             '3a312e3400000000110000006f72672e626c75657a2e44657669636531000000'
   -1   381             '5400000000000000040000005253534900016e00aaff0000100000004d616e75'
   -1   382             '6661637475726572446174610005617b71767d00240000007500026179000000'
   -1   383             '180000004204010170d0c24e08ab57d2c24e08ab560100000000000000000000'
   -1   384         ),
   -1   385         'marshal': False,
   -1   386     },
   -1   387 ]
   -1   388 
   -1   389 
   -1   390 class TestMarshal(unittest.TestCase):
   -1   391     maxDiff = 1000
   -1   392 
   -1   393     def test_marshal(self):
   -1   394         for i, item in enumerate(MESSAGES):
   -1   395             msg = item['message']
   -1   396             data = (item['data'], [])
   -1   397 
   -1   398             with self.subTest(i=i, action='unmarshal'):
   -1   399                 self.assertEqual((msg, b'', []), Msg.unmarshal(*data))
   -1   400 
   -1   401             # marshal output is not well defined (e.g. sorting)
   -1   402             if item.get('marshal', True):
   -1   403                 with self.subTest(i=i, action='marshal'):
   -1   404                     self.assertEqual(msg.marshal(), data)
   -1   405 
   -1   406     def test_invalid_signature(self):
   -1   407         for sig in ['a(', 'a{yyy}', 'X']:
   -1   408             msg = Msg(
   -1   409                 MsgType.METHOD_CALL,
   -1   410                 1,
   -1   411                 destination='org.freedesktop.DBus',
   -1   412                 path='/org/freedesktop/DBus',
   -1   413                 iface='org.freedesktop.DBus',
   -1   414                 member='Hello',
   -1   415                 sig=sig,
   -1   416                 body=(None,)
   -1   417             )
   -1   418             with self.subTest(sig=sig):
   -1   419                 with self.assertRaises(ValueError):
   -1   420                     msg.marshal()
   -1   421 
   -1   422     def test_wrong_version(self):
   -1   423         data = bytes.fromhex(
   -1   424             '6c01000200000000010000006d00000001016f00150000002f6f72672f667265'
   -1   425             '656465736b746f702f4442757300000002017300140000006f72672e66726565'
   -1   426             '6465736b746f702e4442757300000000030173000500000048656c6c6f000000'
   -1   427             '06017300140000006f72672e667265656465736b746f702e4442757300000000'
   -1   428         )
   -1   429         with self.assertRaises(ValueError):
   -1   430             Msg.unmarshal(data, [])
   -1   431 
   -1   432     def test_remaining_data(self):
   -1   433         data = MESSAGES[0]['data'] + b'\x00'
   -1   434         _, tail, tail_fds = Msg.unmarshal(data, [1])
   -1   435         self.assertEqual(tail, b'\x00')
   -1   436         self.assertEqual(tail_fds, [1])