ya2 · news · projects · code · about

do not do asserts at runtime
[pmachines.git] / pmachines / app.py
index 54b9660fd208c2534afdb99fad9258d989a4943c..994b0fab35546bcad438ee5b0a84db7434ff6b0f 100755 (executable)
@@ -8,18 +8,32 @@ from sys import platform, argv, exit
 from logging import info, debug
 from os.path import exists
 from os import makedirs
+from multiprocessing import cpu_count
 from panda3d.core import Filename, load_prc_file_data, AntialiasAttrib, \
-    Texture, WindowProperties, LVector2i
+    Texture, WindowProperties, LVector2i, TextNode
 from panda3d.bullet import BulletWorld, BulletDebugNode
 from direct.showbase.ShowBase import ShowBase
+from direct.gui.OnscreenText import OnscreenText
 from direct.fsm.FSM import FSM
-from pmachines.music import MusicMgr
+from pmachines.audio.music import MusicMgr
 from pmachines.items.background import Background
-from pmachines.menu import Menu
+from pmachines.gui.menu import Menu
 from pmachines.scene import Scene
-from lib.dictfile import DctFile
-from lib.lib.p3d.p3d import LibP3d
-from lib.engine.lang import LangMgr
+from pmachines.scenes.scene_basketball import SceneBasketBall
+from pmachines.scenes.scene_box import SceneBox
+from pmachines.scenes.scene_domino_box_basketball import SceneDominoBoxBasketball
+from pmachines.scenes.scene_domino_box import SceneDominoBox
+from pmachines.scenes.scene_domino import SceneDomino
+from pmachines.scenes.scene_teeter_domino_box_basketball import SceneTeeterDominoBoxBasketball
+from pmachines.scenes.scene_teeter_tooter import SceneTeeterTooter
+from pmachines.posmgr import PositionMgr
+from ya2.utils.dictfile import DctFile
+from ya2.p3d.p3d import LibP3d
+from ya2.utils.lang import LangMgr
+from ya2.utils.log import LogMgr
+from ya2.utils.functional import FunctionalTest
+from ya2.p3d.asserts import assert_threads, assert_tasks, assert_render3d, \
+    assert_render2d, assert_aspect2d, assert_events, assert_buffers
 
 
 class MainFsm(FSM):
@@ -33,42 +47,62 @@ class MainFsm(FSM):
 
     def exitMenu(self):
         self._pmachines.on_menu_exit()
+        self.__do_asserts()
 
     def enterScene(self, cls):
         self._pmachines.on_scene_enter(cls)
 
     def exitScene(self):
         self._pmachines.on_scene_exit()
+        self.__do_asserts()
+
+    def __do_asserts(self):
+        args = self._pmachines._args
+        if not LibP3d.runtime() or args.functional_test or args.functional_ref:
+            assert_threads()
+            assert_tasks()
+            assert_render3d()
+            assert_render2d()
+            assert_aspect2d()
+            assert_events()
+            assert_buffers()
 
 
 class PmachinesApp:
 
+    scenes = [
+        SceneDomino,
+        SceneBox,
+        SceneDominoBox,
+        SceneBasketBall,
+        SceneDominoBoxBasketball,
+        SceneTeeterTooter,
+        SceneTeeterDominoBoxBasketball]
+
     def __init__(self):
         info('platform: %s' % platform)
         info('exists main.py: %s' % exists('main.py'))
-        args = self._parse_args()
+        self._args = args = self._parse_args()
         self._configure(args)
         self.base = ShowBase()
-        self._prepare_window(args)
+        self._pipeline = None
         self.updating = args.update
         self.version = args.version
+        self.log_mgr = LogMgr.init_cls()()
+        self._pos_mgr = PositionMgr()
+        self._prepare_window(args)
         if args.update:
             return
+        if args.functional_test:
+            self._options['settings']['volume'] = 0
         self._music = MusicMgr(self._options['settings']['volume'])
         self.lang_mgr = LangMgr(self._options['settings']['language'],
                                 'pmachines',
                                 'assets/locale/')
         self._fsm = MainFsm(self)
         if args.screenshot:
-            scene_classes = []
-            for _file in glob('pmachines/scenes/*.py'):
-                _fn = _file.replace('.py', '').replace('/', '.')
-                for member in import_module(_fn).__dict__.values():
-                    if isclass(member) and issubclass(member, Scene) and \
-                            member != Scene:
-                        scene_classes += [member]
-            cls = [cls for cls in scene_classes if cls.__name__ == args.screenshot][0]
-            scene = cls(BulletWorld(), None, True, False, lambda: None)
+            cls = [cls for cls in self.scenes if cls.__name__ == args.screenshot][0]
+            scene = cls(BulletWorld(), None, True, False, lambda: None, self.scenes)
             scene.screenshot()
             scene.destroy()
             exit()
@@ -81,12 +115,18 @@ class PmachinesApp:
             self._fsm.demand('Scene', cls)
         else:
             self._fsm.demand('Menu')
+        if args.functional_test or args.functional_ref:
+            FunctionalTest(args.functional_ref, self._pos_mgr)
+        if not LibP3d.runtime() or args.functional_test or args.functional_ref:
+            self.__fps_lst = []
+            taskMgr.do_method_later(1.0, self.__assert_fps, 'assert_fps')
 
     def on_menu_enter(self):
         self._menu_bg = Background()
         self._menu = Menu(
             self._fsm, self.lang_mgr, self._options, self._music,
-            self._pipeline)
+            self._pipeline, self.scenes, self._args.functional_test or self._args.functional_ref,
+            self._pos_mgr)
 
     def on_home(self):
         self._fsm.demand('Menu')
@@ -101,7 +141,11 @@ class PmachinesApp:
             self.world, self.on_home,
             self._options['development']['auto_close_instructions'],
             self._options['development']['debug_items'],
-            self.reload)
+            self.reload,
+            self.scenes,
+            self._pos_mgr,
+            self._args.functional_test or self._args.functional_ref,
+            self._options['development']['mouse_coords'])
 
     def on_scene_exit(self):
         self._unset_physics()
@@ -114,6 +158,9 @@ class PmachinesApp:
         load_prc_file_data('', 'window-title pmachines')
         load_prc_file_data('', 'framebuffer-srgb true')
         load_prc_file_data('', 'sync-video true')
+        if args.functional_test or args.functional_ref:
+            load_prc_file_data('', 'win-size 1360 768')
+            # otherwise it is not centered in exwm
         # load_prc_file_data('', 'threading-model Cull/Draw')
         # it freezes when you go to the next scene
         if args.screenshot:
@@ -126,6 +173,8 @@ class PmachinesApp:
         parser.add_argument('--version', action='store_true')
         parser.add_argument('--optfile')
         parser.add_argument('--screenshot')
+        parser.add_argument('--functional-test', action='store_true')
+        parser.add_argument('--functional-ref', action='store_true')
         cmd_line = [arg for arg in iter(argv[1:]) if not arg.startswith('-psn_')]
         args = parser.parse_args(cmd_line)
         return args
@@ -161,6 +210,7 @@ class PmachinesApp:
                 'auto_close_instructions': 0,
                 'show_buffers': 0,
                 'debug_items': 0,
+                'mouse_coords': 0,
                 'fps': 0}}
         opt_path = LibP3d.fixpath(data_path + '/' + optfile) if data_path else optfile
         opt_exists = exists(opt_path)
@@ -173,21 +223,27 @@ class PmachinesApp:
         if res:
             res = LVector2i(*[int(_res) for _res in res.split('x')])
         else:
-            d_i = base.pipe.get_display_information()
-            def _res(idx):
-                return d_i.get_display_mode_width(idx), \
-                    d_i.get_display_mode_height(idx)
-            resolutions = [
-                _res(idx) for idx in range(d_i.get_total_display_modes())]
+            resolutions = []
+            if not self.version:
+                d_i = base.pipe.get_display_information()
+                def _res(idx):
+                    return d_i.get_display_mode_width(idx), \
+                        d_i.get_display_mode_height(idx)
+                resolutions = [
+                    _res(idx) for idx in range(d_i.get_total_display_modes())]
             res = sorted(resolutions)[-1]
+        fullscreen = self._options['settings']['fullscreen']
         props = WindowProperties()
-        props.set_size(res)
-        props.set_fullscreen(self._options['settings']['fullscreen'])
-        props.set_icon_filename('assets/icon/pmachines.ico')
-        if not args.screenshot:
+        if args.functional_test or args.functional_ref:
+            fullscreen = False
+        else:
+            props.set_size(res)
+        props.set_fullscreen(fullscreen)
+        props.set_icon_filename('assets/images/icon/pmachines.ico')
+        if not args.screenshot and not self.version:
             base.win.request_properties(props)
         #gltf.patch_loader(base.loader)
-        if self._options['development']['simplepbr']:
+        if self._options['development']['simplepbr'] and not self.version:
             self._pipeline = simplepbr.init(
                 use_normal_maps=True,
                 use_emission_maps=False,
@@ -205,6 +261,16 @@ class PmachinesApp:
             base.set_frame_rate_meter(True)
         #self.base.accept('window-event', self._on_win_evt)
         self.base.accept('aspectRatioChanged', self._on_aspect_ratio_changed)
+        if self._options['development']['mouse_coords']:
+            coords_txt = OnscreenText(
+                '', parent=base.a2dTopRight, scale=0.04,
+                pos=(-.03, -.06), fg=(.9, .9, .9, 1), align=TextNode.A_right)
+            def update_coords(task):
+                txt = '%s %s' % (int(base.win.get_pointer(0).x),
+                                 int(base.win.get_pointer(0).y))
+                coords_txt['text'] = txt
+                return task.cont
+            taskMgr.add(update_coords, 'update_coords')
 
     def _set_physics(self):
         if self._options['development']['physics_debug']:
@@ -234,3 +300,12 @@ class PmachinesApp:
     def _on_aspect_ratio_changed(self):
         if self._fsm.state == 'Scene':
             self._scene.on_aspect_ratio_changed()
+
+    def __assert_fps(self, task):
+        if len(self.__fps_lst) > 3:
+            self.__fps_lst.pop(0)
+        self.__fps_lst += [globalClock.average_frame_rate]
+        if len(self.__fps_lst) == 4:
+            fps_threshold = 55 if cpu_count() >= 4 else 25
+            assert(any(fps > fps_threshold for fps in self.__fps_lst), 'low fps %s' % self.__fps_lst)
+        return task.again