Browse Source

fix thread and eventloop for onec args

pikastech 2 years ago
parent
commit
70aabb81ad

+ 4 - 2
examples/_thread/test2.py

@@ -3,15 +3,17 @@ import _thread
 
 finished = False
 
-def test_thread():
+def test_thread(arg):
     global finished
     for i in range(3):
         print(i)
         time.sleep(0.1)
     finished = True
+    print('test_thread arg:', arg)
+    assert arg == 'test'
 
 # 开启线程 获取数据
-_thread.start_new_thread(test_thread, ())
+_thread.start_new_thread(test_thread, ('test'))
 while not finished:
     time.sleep(0.1)
 time.sleep(0.1)

+ 7 - 1
examples/eventloop/test1.py

@@ -3,20 +3,26 @@ from eventloop import EventLoop
 
 finished = False
 
+eventloop.set_debug(True)
+
+
 def test_func(arg1, arg2):
     print("Running test function with arguments:", arg1, arg2)
     return arg1 + arg2
 
+
 def test_callback(res):
     global finished
     print("Running test callback function", res)
     assert res == "Hello World"
     finished = True
 
+
 # Test case 1: Add and run a one-time task
 event_loop = EventLoop(period_ms=100)
 
-event_loop.add_task_once("test_task_once", test_func, test_callback, args=("Hello", " World"))
+event_loop.start_new_task_once(
+    test_func, ("Hello", " World"), callback=test_callback)
 event_loop.start()
 
 # Sleep for enough time to allow the one-time task to run

+ 21 - 3
examples/eventloop/test2.py

@@ -3,25 +3,42 @@ from eventloop import EventLoop
 
 run_time = 0
 
+eventloop.set_debug(True)
+
+
 def test_func(arg1, arg2):
     global run_time
     run_time += 1
     print("Running test function with arguments:", arg1, arg2)
     return arg1 + arg2
 
+
 def test_func2(arg1, arg2):
     print("test function 2 with arguments:", arg1, arg2)
     return arg1 + arg2
 
+
 def test_callback(res):
     print("Running test callback function", res)
     assert res == "Hello World"
 
+
 # Test case 2: Add and run a periodic task
 event_loop = EventLoop(period_ms=100)
 
-event_loop.add_task_periodic("test_task_periodic", test_func, 1000, test_callback, args=("Hello", " World"))
-event_loop.add_task_periodic("test_task_periodic2", test_func2, 200, test_callback, args=("Hello", " World"))
+event_loop.start_new_task_periodic(
+    test_func, ("Hello", " World"),
+    period_ms=1000,
+    callback=test_callback
+)
+
+event_loop.start_new_task(
+    test_func2, ("Hello", " World"),
+    is_periodic=True,
+    period_ms=200,
+    callback=test_callback
+)
+
 event_loop.start()
 
 # Sleep for enough time to allow the periodic task to run multiple times
@@ -31,7 +48,8 @@ while run_time < 3:
 event_loop.stop()
 
 # Test case 3: Test removing a task
-event_loop.add_task_periodic("test_task_remove", test_func, 1000, test_callback, args=("Hello", " World"))
+event_loop.start_new_task_periodic(
+    test_func, ("Hello", " World"), callback=test_callback, task_name="test_task_remove")
 event_loop.remove_task("test_task_remove")
 
 print(event_loop._tasks)

+ 45 - 0
examples/eventloop/test3.py

@@ -0,0 +1,45 @@
+import time
+import eventloop
+
+run_time = 0
+
+eventloop.set_debug(True)
+
+
+def test_func(arg1, arg2):
+    global run_time
+    run_time += 1
+    print("Running test function with arguments:", arg1, arg2)
+    return arg1 + arg2
+
+
+def test_func2():
+    print("test function 2")
+
+
+def test_func3(arg):
+    print("test function 3 with argument:", arg)
+
+
+def test_callback(res):
+    print("Running test callback function", res)
+    assert res == "Hello World"
+
+
+eventloop.start_new_task(test_func, ("Hello", " World"))
+
+eventloop.start_new_task_periodic(
+    test_func2, (),
+    period_ms=200
+)
+
+eventloop.start_new_task_once(test_func3, ("Hello"))
+
+# Sleep for enough time to allow the periodic task to run multiple times
+while run_time < 3:
+    time.sleep(0.1)
+
+# Test case 3: Test removing a task
+eventloop.start_new_task_periodic(
+    test_func, ("Hello", " World"), callback=test_callback, task_name="test_task_remove")
+eventloop.remove_task("test_task_remove")

+ 8 - 3
package/_thread/_thread.c

@@ -89,11 +89,16 @@ void _thread_start_new_thread(PikaObj* self, Arg* function, Arg* args_) {
     pika_platform_memset(info, 0, sizeof(pika_thread_info));
     info->function = arg_copy(function);
 
-    PikaObj* tuple = arg_getPtr(args_);
-    size_t tuple_size = PikaStdData_Tuple_len(tuple);
-    if (tuple_size > 0) {
+    if (arg_isObject(args_)) {
+        PikaObj* tuple = arg_getPtr(args_);
+        size_t tuple_size = PikaStdData_Tuple_len(tuple);
+        if (tuple_size > 0) {
+            info->args = arg_copy(args_);
+        }
+    } else {
         info->args = arg_copy(args_);
     }
+
     _VM_lock_init();
     info->stack_size = g_thread_stack_size;
     info->thread = pika_platform_thread_init("pika_thread", _thread_func, info,

+ 128 - 13
package/eventloop/eventloop.py

@@ -1,6 +1,8 @@
 import _thread
 import time
 
+_is_debug = False
+
 
 class EventTask:
     """
@@ -25,7 +27,6 @@ class EventTask:
         self._args = args
         self._period_ms = period_ms
         if period_ms != None:
-            # print('period_ms', period_ms)
             self._is_periodic = True
 
 
@@ -38,6 +39,7 @@ class EventLoop:
     _thread_stack = 0
     _need_stop = False
     _started = False
+    _uuid = 0
 
     def __init__(self, period_ms=100, thread_stack=0):
         """
@@ -47,11 +49,33 @@ class EventLoop:
         self._period_ms = period_ms
         self._thread_stack = thread_stack
 
-    def _add_task(self, task_name, func, callback=None, args=None, period_ms=None):
+    def _add_task(self, task_name, func, callback, args, period_ms):
+        if task_name == None:
+            self._uuid += 1
+            task_name = str(self._uuid)
+        _debug('add_task', task_name)
+        _debug('func', func)
+        _debug('callback', callback)
+        _debug('args', args)
+        _debug('period_ms', period_ms)
         new_task = EventTask(func, callback, args, period_ms)
         self._tasks[task_name] = new_task
 
-    def add_task_once(self, task_name, func, callback=None, args=None):
+    def start_new_task(self, func, args, is_periodic=True, period_ms=1000, callback=None, task_name=None):
+        """
+        Add a task to EventLoop
+        :param task_name: name of task
+        :param func: function to be called
+        :param period_ms: period of task
+        :param callback: callback function
+        :param args: arguments of func
+        """
+        if is_periodic:
+            self._add_task(task_name, func, callback, args, period_ms)
+        else:
+            self._add_task(task_name, func, callback, args, None)
+
+    def start_new_task_once(self, func, args, callback=None, task_name=None):
         """
         Add a task to EventLoop, run once
         :param task_name: name of task
@@ -59,9 +83,9 @@ class EventLoop:
         :param callback: callback function
         :param args: arguments of func
         """
-        self._add_task(task_name, func, callback, args, None)
+        self.start_new_task(func, args, False, None, callback, task_name)
 
-    def add_task_periodic(self, task_name, func, period_ms=1000, callback=None, args=None):
+    def start_new_task_periodic(self, func, args, period_ms=1000, callback=None, task_name=None):
         """
         Add a task to EventLoop, run periodically
         :param task_name: name of task
@@ -70,7 +94,7 @@ class EventLoop:
         :param callback: callback function
         :param args: arguments of func
         """
-        self._add_task(task_name, func, callback, args, period_ms)
+        self.start_new_task(func, args, True, period_ms, callback, task_name)
 
     def remove_task(self, task_name):
         """
@@ -79,22 +103,21 @@ class EventLoop:
         """
         self._tasks.remove(task_name)
 
+    def _run_task(self, task: EventTask):
+        _res = task._func(*task._args)
+        if task._callback != None:
+            task._callback(_res)
 
     def _run_thread(self):
         while not self._need_stop:
             tick = time.tick_ms()
             for task_name, task in self._tasks.items():
                 if task._is_periodic:
-                    # print('periodic', task_name, tick, task._last_call_time, task._period_ms)
                     if tick - task._last_call_time > task._period_ms:
-                        _res = task._func(*task._args)
-                        if task._callback != None:
-                            task._callback(_res)
+                        self._run_task(task)
                         task._last_call_time = tick
                 else:
-                    _res = task._func(*task._args)
-                    if task._callback != None:
-                        task._callback(_res)
+                    self._run_task(task)
                     self.remove_task(task_name)
             if self._need_stop:
                 break
@@ -126,3 +149,95 @@ class EventLoop:
             time.sleep(0.1)
         time.sleep(1)
 
+
+def set_debug(enable: bool):
+    global _is_debug
+    _is_debug = enable
+
+
+def _debug(*args):
+    if _is_debug:
+        print('\x1b[33m[eventloop]', *args, "\x1b[0m")
+
+
+g_default_event_loop: EventLoop = None
+
+
+def _get_default_event_loop():
+    global g_default_event_loop
+    if g_default_event_loop == None:
+        g_default_event_loop = EventLoop()
+        g_default_event_loop.start()
+    return g_default_event_loop
+
+
+def start_new_task(func, args, is_periodic=True, period_ms=1000, callback=None, task_name=None):
+    """
+    Add a task to EventLoop
+    :param task_name: name of task
+    :param func: function to be called
+    :param period_ms: period of task
+    :param callback: callback function
+    :param args: arguments of func
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start_new_task(func, args, is_periodic,
+                             period_ms, callback, task_name)
+
+
+def start_new_task_once(func, args, callback=None, task_name=None):
+    """
+    Add a task to EventLoop, run once
+    :param task_name: name of task
+    :param func: function to be called
+    :param callback: callback function
+    :param args: arguments of func
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start_new_task_once(func, args, callback, task_name)
+
+
+def start_new_task_periodic(func, args, period_ms=1000, callback=None, task_name=None):
+    """
+    Add a task to EventLoop, run periodically
+    :param task_name: name of task
+    :param func: function to be called
+    :param period_ms: period of task
+    :param callback: callback function
+    :param args: arguments of func
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start_new_task_periodic(
+        func, args, period_ms, callback, task_name)
+
+
+def remove_task(task_name):
+    """
+    Remove a task from EventLoop
+    :param task_name: name of task
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.remove_task(task_name)
+
+
+def stop():
+    """
+    Stop EventLoop
+    """
+    if g_default_event_loop == None:
+        return
+    _debug('stop default eventloop')
+    eventloop = _get_default_event_loop()
+    eventloop.stop()
+
+
+def start():
+    """
+    Start EventLoop
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start()
+
+
+def __del__():
+    stop()

+ 1 - 1
port/linux/.vscode/launch.json

@@ -17,7 +17,7 @@
                 // "--gtest_filter=os.path"
                 // "--gtest_filter=packtool.packread"
                 // "--gtest_filter=parser.char_issue1"
-                "--gtest_filter=eventloop.test2"
+                "--gtest_filter=eventloop.test1"
             ],
             "stopAtEntry": false,
             "cwd": "${workspaceFolder}",

+ 128 - 13
port/linux/package/pikascript/eventloop.py

@@ -1,6 +1,8 @@
 import _thread
 import time
 
+_is_debug = False
+
 
 class EventTask:
     """
@@ -25,7 +27,6 @@ class EventTask:
         self._args = args
         self._period_ms = period_ms
         if period_ms != None:
-            # print('period_ms', period_ms)
             self._is_periodic = True
 
 
@@ -38,6 +39,7 @@ class EventLoop:
     _thread_stack = 0
     _need_stop = False
     _started = False
+    _uuid = 0
 
     def __init__(self, period_ms=100, thread_stack=0):
         """
@@ -47,11 +49,33 @@ class EventLoop:
         self._period_ms = period_ms
         self._thread_stack = thread_stack
 
-    def _add_task(self, task_name, func, callback=None, args=None, period_ms=None):
+    def _add_task(self, task_name, func, callback, args, period_ms):
+        if task_name == None:
+            self._uuid += 1
+            task_name = str(self._uuid)
+        _debug('add_task', task_name)
+        _debug('func', func)
+        _debug('callback', callback)
+        _debug('args', args)
+        _debug('period_ms', period_ms)
         new_task = EventTask(func, callback, args, period_ms)
         self._tasks[task_name] = new_task
 
-    def add_task_once(self, task_name, func, callback=None, args=None):
+    def start_new_task(self, func, args, is_periodic=True, period_ms=1000, callback=None, task_name=None):
+        """
+        Add a task to EventLoop
+        :param task_name: name of task
+        :param func: function to be called
+        :param period_ms: period of task
+        :param callback: callback function
+        :param args: arguments of func
+        """
+        if is_periodic:
+            self._add_task(task_name, func, callback, args, period_ms)
+        else:
+            self._add_task(task_name, func, callback, args, None)
+
+    def start_new_task_once(self, func, args, callback=None, task_name=None):
         """
         Add a task to EventLoop, run once
         :param task_name: name of task
@@ -59,9 +83,9 @@ class EventLoop:
         :param callback: callback function
         :param args: arguments of func
         """
-        self._add_task(task_name, func, callback, args, None)
+        self.start_new_task(func, args, False, None, callback, task_name)
 
-    def add_task_periodic(self, task_name, func, period_ms=1000, callback=None, args=None):
+    def start_new_task_periodic(self, func, args, period_ms=1000, callback=None, task_name=None):
         """
         Add a task to EventLoop, run periodically
         :param task_name: name of task
@@ -70,7 +94,7 @@ class EventLoop:
         :param callback: callback function
         :param args: arguments of func
         """
-        self._add_task(task_name, func, callback, args, period_ms)
+        self.start_new_task(func, args, True, period_ms, callback, task_name)
 
     def remove_task(self, task_name):
         """
@@ -79,22 +103,21 @@ class EventLoop:
         """
         self._tasks.remove(task_name)
 
+    def _run_task(self, task: EventTask):
+        _res = task._func(*task._args)
+        if task._callback != None:
+            task._callback(_res)
 
     def _run_thread(self):
         while not self._need_stop:
             tick = time.tick_ms()
             for task_name, task in self._tasks.items():
                 if task._is_periodic:
-                    # print('periodic', task_name, tick, task._last_call_time, task._period_ms)
                     if tick - task._last_call_time > task._period_ms:
-                        _res = task._func(*task._args)
-                        if task._callback != None:
-                            task._callback(_res)
+                        self._run_task(task)
                         task._last_call_time = tick
                 else:
-                    _res = task._func(*task._args)
-                    if task._callback != None:
-                        task._callback(_res)
+                    self._run_task(task)
                     self.remove_task(task_name)
             if self._need_stop:
                 break
@@ -126,3 +149,95 @@ class EventLoop:
             time.sleep(0.1)
         time.sleep(1)
 
+
+def set_debug(enable: bool):
+    global _is_debug
+    _is_debug = enable
+
+
+def _debug(*args):
+    if _is_debug:
+        print('\x1b[33m[eventloop]', *args, "\x1b[0m")
+
+
+g_default_event_loop: EventLoop = None
+
+
+def _get_default_event_loop():
+    global g_default_event_loop
+    if g_default_event_loop == None:
+        g_default_event_loop = EventLoop()
+        g_default_event_loop.start()
+    return g_default_event_loop
+
+
+def start_new_task(func, args, is_periodic=True, period_ms=1000, callback=None, task_name=None):
+    """
+    Add a task to EventLoop
+    :param task_name: name of task
+    :param func: function to be called
+    :param period_ms: period of task
+    :param callback: callback function
+    :param args: arguments of func
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start_new_task(func, args, is_periodic,
+                             period_ms, callback, task_name)
+
+
+def start_new_task_once(func, args, callback=None, task_name=None):
+    """
+    Add a task to EventLoop, run once
+    :param task_name: name of task
+    :param func: function to be called
+    :param callback: callback function
+    :param args: arguments of func
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start_new_task_once(func, args, callback, task_name)
+
+
+def start_new_task_periodic(func, args, period_ms=1000, callback=None, task_name=None):
+    """
+    Add a task to EventLoop, run periodically
+    :param task_name: name of task
+    :param func: function to be called
+    :param period_ms: period of task
+    :param callback: callback function
+    :param args: arguments of func
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start_new_task_periodic(
+        func, args, period_ms, callback, task_name)
+
+
+def remove_task(task_name):
+    """
+    Remove a task from EventLoop
+    :param task_name: name of task
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.remove_task(task_name)
+
+
+def stop():
+    """
+    Stop EventLoop
+    """
+    if g_default_event_loop == None:
+        return
+    _debug('stop default eventloop')
+    eventloop = _get_default_event_loop()
+    eventloop.stop()
+
+
+def start():
+    """
+    Start EventLoop
+    """
+    eventloop = _get_default_event_loop()
+    eventloop.start()
+
+
+def __del__():
+    stop()

+ 8 - 3
port/linux/package/pikascript/pikascript-lib/_thread/_thread.c

@@ -89,11 +89,16 @@ void _thread_start_new_thread(PikaObj* self, Arg* function, Arg* args_) {
     pika_platform_memset(info, 0, sizeof(pika_thread_info));
     info->function = arg_copy(function);
 
-    PikaObj* tuple = arg_getPtr(args_);
-    size_t tuple_size = PikaStdData_Tuple_len(tuple);
-    if (tuple_size > 0) {
+    if (arg_isObject(args_)) {
+        PikaObj* tuple = arg_getPtr(args_);
+        size_t tuple_size = PikaStdData_Tuple_len(tuple);
+        if (tuple_size > 0) {
+            info->args = arg_copy(args_);
+        }
+    } else {
         info->args = arg_copy(args_);
     }
+
     _VM_lock_init();
     info->stack_size = g_thread_stack_size;
     info->thread = pika_platform_thread_init("pika_thread", _thread_func, info,

+ 6 - 3
src/PikaVM.c

@@ -1335,7 +1335,10 @@ static int _get_n_input_with_unpack(VMState* vm, int n_used) {
             break;
         }
         if (arg_getIsStarred(call_arg)) {
-            pika_assert(arg_isObject(call_arg));
+            if (!arg_isObject(call_arg)) {
+                stack_pushArg(&stack_tmp, arg_copy(call_arg));
+                goto __continue;
+            }
             PikaObj* obj = arg_getPtr(call_arg);
             int len = _obj_getLen(obj);
             for (int i_star_arg = len - 1; i_star_arg >= 0; i_star_arg--) {
@@ -2661,12 +2664,12 @@ static Arg* VM_instruction_handler_OPT(PikaObj* self,
                 op.res = arg_setInt(op.res, "", op.i1 % op.i2);
                 goto exit;
             }
-            #if PIKA_MATH_ENABLE
+#if PIKA_MATH_ENABLE
             if (op.t1 == ARG_TYPE_FLOAT || op.t2 == ARG_TYPE_FLOAT) {
                 op.res = arg_setFloat(op.res, "", fmod(op.f1, op.f2));
                 goto exit;
             }
-            #endif
+#endif
             VMState_setErrorCode(vm, PIKA_RES_ERR_OPERATION_FAILED);
             pika_platform_printf(
                 "TypeError: unsupported operand type(s) for %%: 'float'\n");

+ 4 - 2
test/python/_thread/test2.py

@@ -3,15 +3,17 @@ import _thread
 
 finished = False
 
-def test_thread():
+def test_thread(arg):
     global finished
     for i in range(3):
         print(i)
         time.sleep(0.1)
     finished = True
+    print('test_thread arg:', arg)
+    assert arg == 'test'
 
 # 开启线程 获取数据
-_thread.start_new_thread(test_thread, ())
+_thread.start_new_thread(test_thread, ('test'))
 while not finished:
     time.sleep(0.1)
 time.sleep(0.1)

+ 7 - 1
test/python/eventloop/test1.py

@@ -3,20 +3,26 @@ from eventloop import EventLoop
 
 finished = False
 
+eventloop.set_debug(True)
+
+
 def test_func(arg1, arg2):
     print("Running test function with arguments:", arg1, arg2)
     return arg1 + arg2
 
+
 def test_callback(res):
     global finished
     print("Running test callback function", res)
     assert res == "Hello World"
     finished = True
 
+
 # Test case 1: Add and run a one-time task
 event_loop = EventLoop(period_ms=100)
 
-event_loop.add_task_once("test_task_once", test_func, test_callback, args=("Hello", " World"))
+event_loop.start_new_task_once(
+    test_func, ("Hello", " World"), callback=test_callback)
 event_loop.start()
 
 # Sleep for enough time to allow the one-time task to run

+ 21 - 3
test/python/eventloop/test2.py

@@ -3,25 +3,42 @@ from eventloop import EventLoop
 
 run_time = 0
 
+eventloop.set_debug(True)
+
+
 def test_func(arg1, arg2):
     global run_time
     run_time += 1
     print("Running test function with arguments:", arg1, arg2)
     return arg1 + arg2
 
+
 def test_func2(arg1, arg2):
     print("test function 2 with arguments:", arg1, arg2)
     return arg1 + arg2
 
+
 def test_callback(res):
     print("Running test callback function", res)
     assert res == "Hello World"
 
+
 # Test case 2: Add and run a periodic task
 event_loop = EventLoop(period_ms=100)
 
-event_loop.add_task_periodic("test_task_periodic", test_func, 1000, test_callback, args=("Hello", " World"))
-event_loop.add_task_periodic("test_task_periodic2", test_func2, 200, test_callback, args=("Hello", " World"))
+event_loop.start_new_task_periodic(
+    test_func, ("Hello", " World"),
+    period_ms=1000,
+    callback=test_callback
+)
+
+event_loop.start_new_task(
+    test_func2, ("Hello", " World"),
+    is_periodic=True,
+    period_ms=200,
+    callback=test_callback
+)
+
 event_loop.start()
 
 # Sleep for enough time to allow the periodic task to run multiple times
@@ -31,7 +48,8 @@ while run_time < 3:
 event_loop.stop()
 
 # Test case 3: Test removing a task
-event_loop.add_task_periodic("test_task_remove", test_func, 1000, test_callback, args=("Hello", " World"))
+event_loop.start_new_task_periodic(
+    test_func, ("Hello", " World"), callback=test_callback, task_name="test_task_remove")
 event_loop.remove_task("test_task_remove")
 
 print(event_loop._tasks)

+ 45 - 0
test/python/eventloop/test3.py

@@ -0,0 +1,45 @@
+import time
+import eventloop
+
+run_time = 0
+
+eventloop.set_debug(True)
+
+
+def test_func(arg1, arg2):
+    global run_time
+    run_time += 1
+    print("Running test function with arguments:", arg1, arg2)
+    return arg1 + arg2
+
+
+def test_func2():
+    print("test function 2")
+
+
+def test_func3(arg):
+    print("test function 3 with argument:", arg)
+
+
+def test_callback(res):
+    print("Running test callback function", res)
+    assert res == "Hello World"
+
+
+eventloop.start_new_task(test_func, ("Hello", " World"))
+
+eventloop.start_new_task_periodic(
+    test_func2, (),
+    period_ms=200
+)
+
+eventloop.start_new_task_once(test_func3, ("Hello"))
+
+# Sleep for enough time to allow the periodic task to run multiple times
+while run_time < 3:
+    time.sleep(0.1)
+
+# Test case 3: Test removing a task
+eventloop.start_new_task_periodic(
+    test_func, ("Hello", " World"), callback=test_callback, task_name="test_task_remove")
+eventloop.remove_task("test_task_remove")

+ 1 - 0
test/thread-test.cpp

@@ -37,6 +37,7 @@ TEST_SINGLE_FILE(thread, test2, "test/python/_thread/test2.py")
 
 TEST_SINGLE_FILE(eventloop, test1, "test/python/eventloop/test1.py")
 TEST_SINGLE_FILE(eventloop, test2, "test/python/eventloop/test2.py")
+TEST_SINGLE_FILE(eventloop, test3, "test/python/eventloop/test3.py")
 
 #endif
 TEST_END