Commits

Jean-Paul Calderone committed 554e5a0

(fijal, glyph, exarkun) Encapsulate cpyext tstate cleanup for tests in a method; fix a bug in that cleanup that made it hard to have more than a single unit test, ever. Port all the tests to be written in C, the language of wonder and beauty.

  • Participants
  • Parent commits 3dd1ad0
  • Branches non-null-threadstate

Comments (0)

Files changed (3)

File pypy/module/cpyext/pystate.py

 # initialization).
 ExecutionContext.cpyext_initialized_threadstate = False
 
+def cleanup_cpyext_state(self):
+    try:
+        del self.cpyext_threadstate
+    except AttributeError:
+        pass
+    self.cpyext_initialized_threadstate = False
+ExecutionContext.cleanup_cpyext_state = cleanup_cpyext_state
+
 class InterpreterState(object):
     def __init__(self, space):
         self.interpreter_state = lltype.malloc(
             PyInterpreterState.TO, flavor='raw', zero=True, immortal=True)
 
-
     def new_thread_state(self, space):
         """
         Create a new ThreadStateCapsule to hold the PyThreadState for a

File pypy/module/cpyext/test/test_cpyext.py

             del obj
         import gc; gc.collect()
 
-        try:
-            del space.getexecutioncontext().cpyext_threadstate
-        except AttributeError:
-            pass
+        space.getexecutioncontext().cleanup_cpyext_state()
 
         for w_obj in state.non_heaptypes_w:
             Py_DecRef(space, w_obj)

File pypy/module/cpyext/test/test_pystate.py

         # Should compile at least
         module.test()
 
+
+    def test_thread_state_get(self):
+        module = self.import_extension('foo', [
+                ("get", "METH_NOARGS",
+                 """
+                     PyThreadState *tstate = PyThreadState_Get();
+                     if (tstate == NULL) {
+                         return PyLong_FromLong(0);
+                     }
+                     if (tstate->interp != PyInterpreterState_Head()) {
+                         return PyLong_FromLong(1);
+                     }
+                     if (tstate->interp->next != NULL) {
+                         return PyLong_FromLong(2);
+                     }
+                     return PyLong_FromLong(3);
+                 """),
+                ])
+        assert module.get() == 3
+
+    def test_basic_threadstate_dance(self):
+        module = self.import_extension('foo', [
+                ("dance", "METH_NOARGS",
+                 """
+                     PyThreadState *old_tstate, *new_tstate;
+
+                     old_tstate = PyThreadState_Swap(NULL);
+                     if (old_tstate == NULL) {
+                         return PyLong_FromLong(0);
+                     }
+
+                     new_tstate = PyThreadState_Get();
+                     if (new_tstate != NULL) {
+                         return PyLong_FromLong(1);
+                     }
+
+                     new_tstate = PyThreadState_Swap(old_tstate);
+                     if (new_tstate != NULL) {
+                         return PyLong_FromLong(2);
+                     }
+
+                     new_tstate = PyThreadState_Get();
+                     if (new_tstate != old_tstate) {
+                         return PyLong_FromLong(3);
+                     }
+
+                     return PyLong_FromLong(4);
+                 """),
+                ])
+        assert module.dance() == 4
+
+    def test_threadstate_dict(self):
+        module = self.import_extension('foo', [
+                ("getdict", "METH_NOARGS",
+                 """
+                 PyObject *dict = PyThreadState_GetDict();
+                 Py_INCREF(dict);
+                 return dict;
+                 """),
+                ])
+        assert isinstance(module.getdict(), dict)
+
+    def test_savethread(self):
+        module = self.import_extension('foo', [
+                ("bounce", "METH_NOARGS",
+                 """
+                 PyThreadState *tstate = PyEval_SaveThread();
+                 if (tstate == NULL) {
+                     return PyLong_FromLong(0);
+                 }
+
+                 if (PyThreadState_Get() != NULL) {
+                     return PyLong_FromLong(1);
+                 }
+
+                 PyEval_RestoreThread(tstate);
+
+                 if (PyThreadState_Get() != tstate) {
+                     return PyLong_FromLong(2);
+                 }
+
+                 return PyLong_FromLong(3);
+                                  """),
+                ])
+
+
+
 class TestInterpreterState(BaseApiTest):
     def test_interpreter_head(self, space, api):
         state = api.PyInterpreterState_Head()
     def test_interpreter_next(self, space, api):
         state = api.PyInterpreterState_Head()
         assert nullptr(PyInterpreterState.TO) == api.PyInterpreterState_Next(state)
-
-
-class DirectThreadStateBase(LeakCheckingTest):
-    # XXX Subclasses of this are probably pretty slow, because creating new
-    # spaces is pretty slow.  They probably leak some memory too, because cpyext
-    # initialization allocates some stuff and it's too hard to find it to clean
-    # it up.
-    # XXX This should be setup_method not setup_class, but mystery failures.
-    def setup_class(cls):
-        # XXX HACK HACK HACK Mystery bug, not going to debug it, just going to hack it
-        leakfinder.TRACK_ALLOCATIONS = True
-        leakfinder.stop_tracking_allocations(check=False)
-
-        # Make a *new* space.  blah blah explain more
-        from pypy.conftest import maketestobjspace, make_config, option
-        cls.space = maketestobjspace(make_config(option, usemodules=["cpyext"]))
-
-
-    def teardown_class(cls):
-        ec = cls.space.getexecutioncontext()
-        del ec.cpyext_threadstate
-        ec.cpyext_initialized_threadstate = False
-
-        leakfinder.start_tracking_allocations()
-
-
-class TestThreadStateDirect(DirectThreadStateBase):
-    def test_thread_state_interp(self):
-        ts = PyThreadState_Get(self.space)
-        assert ts.c_interp == PyInterpreterState_Head(self.space)
-        assert ts.c_interp.c_next == nullptr(PyInterpreterState.TO)
-
-    def test_thread_state_get(self):
-        return
-        ts = PyThreadState_Get(self.space)
-        assert ts != nullptr(PyThreadState.TO)
-
-
-    def test_basic_threadstate_dance(self, space, api):
-        return
-        # Let extension modules call these functions,
-        # Not sure of the semantics in pypy though.
-        # (cpyext always acquires and releases the GIL around calls)
-        tstate = api.PyThreadState_Swap(None)
-        assert tstate is not None
-
-        assert api.PyThreadState_Get() is None
-        assert api.PyThreadState_Swap(tstate) is None
-        assert api.PyThreadState_Get() is tstate
-
-        api.PyEval_AcquireThread(tstate)
-        api.PyEval_ReleaseThread(tstate)
-
-    def test_threadstate_dict(self, space, api):
-        return
-        ts = api.PyThreadState_Get()
-        ref = ts.c_dict
-        assert ref == api.PyThreadState_GetDict()
-        w_obj = from_ref(space, ref)
-        assert space.isinstance_w(w_obj, space.w_dict)
-
-    def test_savethread(self, space, api):
-        return
-        ts = api.PyEval_SaveThread()
-        assert ts
-        assert api.PyThreadState_Get() == nullptr(PyThreadState.TO)
-        api.PyEval_RestoreThread(ts)
-        assert api.PyThreadState_Get() != nullptr(PyThreadState.TO)
-
-del LeakCheckingTest