Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 136 additions & 5 deletions Lib/test/test_capi/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
NULL = None

class CAPITest(unittest.TestCase):
# TODO: Test the following functions:
#
# PySys_Audit()
# PySys_AuditTuple()

maxDiff = None

@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
Expand Down Expand Up @@ -211,6 +206,142 @@ def test_sys_writestderr(self):
# Test PySys_WriteStderr()
self._test_sys_writestream('PySys_WriteStderr', 'stderr')

@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
def test_sys_audit(self):
sys_audit = _testlimitedcapi.sys_audit

audit_events = []
def audit_hook(event, args):
audit_events.append((event, args))
return None

import sys
sys.addaudithook(audit_hook)

try:
result = sys_audit("cpython.run_command", "")
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 1)
self.assertEqual(audit_events[-1][0], "cpython.run_command")
self.assertEqual(audit_events[-1][1], ())

result = sys_audit("open", "OOO", "test.txt", "r", 0)
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 2)
self.assertEqual(audit_events[-1][0], "open")
self.assertEqual(len(audit_events[-1][1]), 3)
self.assertEqual(audit_events[-1][1][0], "test.txt")
self.assertEqual(audit_events[-1][1][1], "r")
self.assertEqual(audit_events[-1][1][2], 0)

test_dict = {"key": "value"}
test_list = [1, 2, 3]
result = sys_audit("test.objects", "OO", test_dict, test_list)
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 3)
self.assertEqual(audit_events[-1][0], "test.objects")
self.assertEqual(audit_events[-1][1][0], test_dict)
self.assertEqual(audit_events[-1][1][1], test_list)

result = sys_audit("test.mixed_types", "OOO", "string", 42, 123456789)
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 4)
self.assertEqual(audit_events[-1][0], "test.mixed_types")
self.assertEqual(audit_events[-1][1][0], "string")
self.assertEqual(audit_events[-1][1][1], 42)
self.assertEqual(audit_events[-1][1][2], 123456789)

finally:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add also tests for sys_audit("test.event", "("), sys_audit("test.event", "&"), sys_audit("test.event", b"\xff") and sys_audit("test.event", "{OO}", [], []).

Add also tests for sys_audit(NULL, "O", 1) and sys_audit("test.event", NULL). If they crash, just add comments.

sys.audit_hooks = []

result = sys_audit("cpython.run_file", "")
self.assertEqual(result, 0)

result = sys_audit("os.chdir", "(O)", "/tmp")
self.assertEqual(result, 0)

result = sys_audit("ctypes.dlopen", "O", "libc.so.6")
self.assertEqual(result, 0)

self.assertRaises(TypeError, sys_audit, 123, "O", "arg")
self.assertRaises(TypeError, sys_audit, None, "O", "arg")
self.assertRaises(TypeError, sys_audit, ["not", "a", "string"], "O", "arg")

self.assertRaises(TypeError, sys_audit, "test.event", 456, "arg")
self.assertRaises(TypeError, sys_audit, "test.event", None, "arg")
self.assertRaises(TypeError, sys_audit, "test.event", {"format": "string"}, "arg")

@unittest.skipIf(_testlimitedcapi is None, 'need _testlimitedcapi module')
def test_sys_audittuple(self):
sys_audittuple = _testlimitedcapi.sys_audittuple

# Test with audit hook to verify internal behavior
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
sys_audittuple = _testlimitedcapi.sys_audittuple
# Test with audit hook to verify internal behavior
# Test PySys_AuditTuple()
sys_audittuple = _testlimitedcapi.sys_audittuple
# Test with audit hook to verify internal behavior

audit_events = []
def audit_hook(event, args):
audit_events.append((event, args))
return None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
return None


import sys
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Import it at the top level.

sys.addaudithook(audit_hook)

try:
result = sys_audittuple("cpython.run_command", ())
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 1)
self.assertEqual(audit_events[-1][0], "cpython.run_command")
self.assertEqual(audit_events[-1][1], ())

result = sys_audittuple("os.chdir", ("/tmp",))
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 2)
self.assertEqual(audit_events[-1][0], "os.chdir")
self.assertEqual(audit_events[-1][1], ("/tmp",))

result = sys_audittuple("open", ("test.txt", "r", 0))
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 3)
self.assertEqual(audit_events[-1][0], "open")
self.assertEqual(audit_events[-1][1], ("test.txt", "r", 0))

test_dict = {"key": "value"}
test_list = [1, 2, 3]
result = sys_audittuple("test.objects", (test_dict, test_list))
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 4)
self.assertEqual(audit_events[-1][0], "test.objects")
self.assertEqual(audit_events[-1][1][0], test_dict)
self.assertEqual(audit_events[-1][1][1], test_list)

result = sys_audittuple("test.complex", ("text", 3.14, True, None))
self.assertEqual(result, 0)
self.assertEqual(len(audit_events), 5)
self.assertEqual(audit_events[-1][0], "test.complex")
self.assertEqual(audit_events[-1][1][0], "text")
self.assertEqual(audit_events[-1][1][1], 3.14)
self.assertEqual(audit_events[-1][1][2], True)
self.assertEqual(audit_events[-1][1][3], None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are too many tests which don't add much to coverage. You only need one test with non-empty tuple, one test with NULL as the second argument, one test with non-tuple as the second argument, and few tests for the event argument: non-ASCII string, non-UTF-8 bytes string, NULL.


finally:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add also test for sys_audittuple(NULL, (1,)). If it crashes, just add a comment, like in other tests above.

sys.audit_hooks = []
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this does not work. There is no way to remove the audit hook. This is why these functions remained untested -- because the only way to test them is to use a subprocess. Look like other audit tests are implemented.


result = sys_audittuple("cpython.run_file", ())
self.assertEqual(result, 0)

result = sys_audittuple("ctypes.dlopen", ("libc.so.6",))
self.assertEqual(result, 0)

result = sys_audittuple("sqlite3.connect", ("test.db",))
self.assertEqual(result, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do these test add in comparison with the above tests?


self.assertRaises(TypeError, sys_audittuple, 123, ("arg",))
self.assertRaises(TypeError, sys_audittuple, None, ("arg",))
self.assertRaises(TypeError, sys_audittuple, ["not", "a", "string"], ("arg",))

self.assertRaises(TypeError, sys_audittuple, "test.event", "not_a_tuple")
self.assertRaises(TypeError, sys_audittuple, "test.event", ["list", "not", "tuple"])
self.assertRaises(TypeError, sys_audittuple, "test.event", {"dict": "not_tuple"})
self.assertRaises(TypeError, sys_audittuple, "test.event", None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests don't test PySys_AuditTuple. They test the wrapper.



if __name__ == "__main__":
unittest.main()
45 changes: 45 additions & 0 deletions Modules/_testlimitedcapi/sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#endif
#include "parts.h"
#include "util.h"
#include "audit.h"


static PyObject *
Expand Down Expand Up @@ -106,6 +107,48 @@ sys_getxoptions(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(ignored))
return Py_XNewRef(result);
}

static PyObject *
sys_audit(PyObject *Py_UNUSED(module), PyObject *args)
{
const char *event;
const char *argFormat;
PyObject *arg1 = NULL, *arg2 = NULL, *arg3 = NULL;

if (!PyArg_ParseTuple(args, "ss|OOO", &event, &argFormat, &arg1, &arg2, &arg3)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use z# for even and format. Test with non-UTF-8 bytestring and with NULL.

return NULL;
}

int result;
if (arg1 == NULL) {
result = PySys_Audit(event, argFormat);
} else if (arg2 == NULL) {
result = PySys_Audit(event, argFormat, arg1);
} else if (arg3 == NULL) {
result = PySys_Audit(event, argFormat, arg1, arg2);
} else {
result = PySys_Audit(event, argFormat, arg1, arg2, arg3);
}

RETURN_INT(result);
}

static PyObject *
sys_audittuple(PyObject *Py_UNUSED(module), PyObject *args)
{
const char *event;
PyObject *tuple_args;

if (!PyArg_ParseTuple(args, "sO", &event, &tuple_args)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make the second argument optional and NULL by default. This will allow you to test with NULL.

Use z# for the first argument. This will allow you to test with non-UTF-8 bytestring and with NULL.

return NULL;
}

if (!PyTuple_Check(tuple_args)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't check the argument here. Let PySys_AuditTuple() to check it. And test it with wrong type of argument.

PyErr_SetString(PyExc_TypeError, "second argument must be a tuple");
return NULL;
}

RETURN_INT(PySys_AuditTuple(event, tuple_args));
}

static PyMethodDef test_methods[] = {
{"sys_getattr", sys_getattr, METH_O},
Expand All @@ -115,6 +158,8 @@ static PyMethodDef test_methods[] = {
{"sys_getobject", sys_getobject, METH_O},
{"sys_setobject", sys_setobject, METH_VARARGS},
{"sys_getxoptions", sys_getxoptions, METH_NOARGS},
{"sys_audit", sys_audit, METH_VARARGS},
{"sys_audittuple", sys_audittuple, METH_VARARGS},
{NULL},
};

Expand Down
Loading