1
0
mirror of https://github.com/samba-team/samba.git synced 2025-03-11 16:58:40 +03:00

pytest:conditional_ace_claims: ease export of failing tests to C

When a test fails, this prints a little stanza like

static void test_something(void **state)
{
       INIT();
       USER_SIDS("WD", "AA");
       DEVICE_SIDS("BA", "BG");
       SD("D:(XA;;0x1f;;;AA;(! Member_of{SID(AA)}))");
       DENY_CHECK(0x10);
}

which is exactly right for copying into
libcli/security/tests/test_run_conditional_ace.c
which is much easier to iterate over with compiling and debugging.

Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz>
Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
Douglas Bagnall 2023-08-23 15:23:29 +12:00 committed by Andrew Bartlett
parent 30e6249d22
commit b65ac10096

View File

@ -32,6 +32,63 @@ DEFAULT_ACCESS2 = (security.SEC_STD_READ_CONTROL |
security.SEC_ADS_READ_PROP)
def write_c_test_on_failure(f):
"""This is a function decorator that writes a function for
/libcli/security/tests/test_run_conditional_ace.c that runs the
equivalent test. Why?! Because iterating over a test to debug the
failure is slower in Python tests, but adding new tests is faster
in Python. So the flow goes like this:
1. add python tests, run them
2. if nothing fails, goto 1
3. copy the test_something() text into test_run_conditional_ace.c,
rename it, and add it to main().
4. `make bin/test_run_conditional_ace && rr bin/test_run_conditional_ace`
5. `rr replay`
and you're away. You can also just work from the Python, but a few
runs of `make -j` after touching something in libcli/security will
make you see why this exists.
You might be thinking that this surely took longer to write than
waiting 100 times for a 30 second compile, but that misses the
point that debugging needs to be ergonomic and fun.
"""
from json import dumps as q # JSON quoting is C quoting, more or less
def wrapper(name, token, sddl, access_desired):
try:
f(name, token, sddl, access_desired)
except Exception:
print()
print('static void test_something(void **state)')
print('{')
print('\tINIT();')
for s in ('sids', 'device_sids'):
if s in token:
macro = ('user_sids' if s == 'sids' else s).upper()
v = ', '.join(q(x) for x in token[s])
print(f'\t{macro}({v});')
for s in ('user_claims', 'device_claims'):
if s in token:
macro = s.upper()
for name, values in token[s].items():
if not isinstance(values, (list, tuple)):
values = [values]
v = ', '.join(q(x) for x in values)
v = q(f"{v}")
print(f'\t{macro}({q(name)}, {v});')
print(f'\tSD({q(sddl)});')
if 'allow' in f.__name__:
print(f'\tALLOW_CHECK({access_desired:#x});')
else:
print(f'\tDENY_CHECK({access_desired:#x});')
print('}')
print()
raise
return wrapper
class ConditionalAceClaimsBase(TestCase):
maxDiff = 0
@ -77,6 +134,7 @@ class ConditionalAceClaimsBase(TestCase):
with open(os.path.join(fuzz_seed_dir, name), 'w') as f:
f.write(sddl)
@write_c_test_on_failure
def _test_allow_with_args(self, _token, sddl, access_desired):
if isinstance(_token, dict):
token = Token(**_token)
@ -94,6 +152,7 @@ class ConditionalAceClaimsBase(TestCase):
self.assertEqual(granted, access_desired)
@write_c_test_on_failure
def _test_deny_with_args(self, token, sddl, access_desired):
if isinstance(token, dict):
token = Token(**token)