From 82d056d7012ac61c5c92528ed95cff593189f280 Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Wed, 28 Feb 2024 01:54:07 +0100 Subject: [PATCH 01/10] Initial work --- arcade/application.py | 5 +- arcade/examples/array_backed_grid.py | 1 - arcade/examples/dual_stick_shooter.py | 7 +- arcade/examples/light_demo.py | 6 +- arcade/examples/particle_fireworks.py | 6 +- arcade/examples/particle_systems.py | 6 +- arcade/examples/perspective.py | 7 +- arcade/examples/pymunk_joint_builder.py | 8 +- arcade/examples/sprite_animated_keyframes.py | 7 +- arcade/examples/sprite_minimal.py | 6 +- arcade/examples/transform_feedback.py | 6 +- arcade/shape_list.py | 3 +- tests/conftest.py | 68 +++++++++++++- tests/integration/examples/test_examples.py | 98 ++++++++++++++++++++ 14 files changed, 219 insertions(+), 15 deletions(-) create mode 100644 tests/integration/examples/test_examples.py diff --git a/arcade/application.py b/arcade/application.py index 21651c913..65d8d7596 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -359,7 +359,10 @@ def center_window(self) -> None: window_width, window_height = self.get_size() # Center the window - self.set_location((screen_width - window_width) // 2, (screen_height - window_height) // 2) + try: + self.set_location((screen_width - window_width) // 2, (screen_height - window_height) // 2) + except Exception: + print("moo") def on_update(self, delta_time: float): """ diff --git a/arcade/examples/array_backed_grid.py b/arcade/examples/array_backed_grid.py index f84fcda98..324737646 100644 --- a/arcade/examples/array_backed_grid.py +++ b/arcade/examples/array_backed_grid.py @@ -104,7 +104,6 @@ def on_mouse_press(self, x, y, button, modifiers): def main(): - MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) arcade.run() diff --git a/arcade/examples/dual_stick_shooter.py b/arcade/examples/dual_stick_shooter.py index c45f8fc16..b966d3126 100644 --- a/arcade/examples/dual_stick_shooter.py +++ b/arcade/examples/dual_stick_shooter.py @@ -338,6 +338,11 @@ def on_draw(self): anchor_y="center") -if __name__ == "__main__": + +def main(): game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) arcade.run() + + +if __name__ == "__main__": + main() diff --git a/arcade/examples/light_demo.py b/arcade/examples/light_demo.py index 60a674e38..3692cae29 100644 --- a/arcade/examples/light_demo.py +++ b/arcade/examples/light_demo.py @@ -303,7 +303,11 @@ def on_update(self, delta_time): self.scroll_screen() -if __name__ == "__main__": +def main(): window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) window.setup() arcade.run() + + +if __name__ == "__main__": + main() diff --git a/arcade/examples/particle_fireworks.py b/arcade/examples/particle_fireworks.py index cb5f98029..446104868 100644 --- a/arcade/examples/particle_fireworks.py +++ b/arcade/examples/particle_fireworks.py @@ -362,6 +362,10 @@ def rocket_smoke_mutator(particle: LifetimeParticle): particle.scale = lerp(0.5, 3.0, particle.lifetime_elapsed / particle.lifetime_original) -if __name__ == "__main__": +def main(): app = FireworksApp() arcade.run() + + +if __name__ == "__main__": + main() diff --git a/arcade/examples/particle_systems.py b/arcade/examples/particle_systems.py index 36590ecc7..3f937d26a 100644 --- a/arcade/examples/particle_systems.py +++ b/arcade/examples/particle_systems.py @@ -766,6 +766,10 @@ def on_key_press(self, key, modifiers): arcade.close_window() -if __name__ == "__main__": +def main(): game = MyGame() arcade.run() + + +if __name__ == "__main__": + main() diff --git a/arcade/examples/perspective.py b/arcade/examples/perspective.py index d2b99e3dc..5f705a7eb 100644 --- a/arcade/examples/perspective.py +++ b/arcade/examples/perspective.py @@ -142,4 +142,9 @@ def on_resize(self, width: int, height: int): self.program["projection"] = Mat4.perspective_projection(self.aspect_ratio, 0.1, 100, fov=75) -Perspective().run() +def main(): + Perspective().run() + + +if __name__ == "__main__": + main() diff --git a/arcade/examples/pymunk_joint_builder.py b/arcade/examples/pymunk_joint_builder.py index 1c45230e1..2720935b9 100644 --- a/arcade/examples/pymunk_joint_builder.py +++ b/arcade/examples/pymunk_joint_builder.py @@ -314,6 +314,10 @@ def on_update(self, delta_time): self.processing_time = timeit.default_timer() - start_time -window = MyApplication(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) +def main(): + window = MyApplication(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) + arcade.run() -arcade.run() + +if __name__ == "__main__": + main() diff --git a/arcade/examples/sprite_animated_keyframes.py b/arcade/examples/sprite_animated_keyframes.py index 7b8bf901a..13c6131a9 100644 --- a/arcade/examples/sprite_animated_keyframes.py +++ b/arcade/examples/sprite_animated_keyframes.py @@ -39,5 +39,10 @@ def on_update(self, delta_time: float): self.sprite.update_animation(delta_time) -if __name__ == "__main__": +def main(): Animated().run() + + +if __name__ == "__main__": + main() + diff --git a/arcade/examples/sprite_minimal.py b/arcade/examples/sprite_minimal.py index 048a45955..e611e8fec 100644 --- a/arcade/examples/sprite_minimal.py +++ b/arcade/examples/sprite_minimal.py @@ -30,6 +30,10 @@ def on_draw(self): self.sprites.draw() -if __name__ == "__main__": +def main(): game = WhiteSpriteCircleExample() game.run() + + +if __name__ == "__main__": + main() diff --git a/arcade/examples/transform_feedback.py b/arcade/examples/transform_feedback.py index b4ce242c0..b4ae25eeb 100644 --- a/arcade/examples/transform_feedback.py +++ b/arcade/examples/transform_feedback.py @@ -147,7 +147,11 @@ def on_draw(self): self.buffer_1, self.buffer_2 = self.buffer_2, self.buffer_1 -if __name__ == "__main__": +def main(): window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) window.center_window() arcade.run() + + +if __name__ == "__main__": + main() diff --git a/arcade/shape_list.py b/arcade/shape_list.py index fcbbd977b..29a53f6a2 100644 --- a/arcade/shape_list.py +++ b/arcade/shape_list.py @@ -30,8 +30,7 @@ from arcade.gl import BufferDescription from arcade.gl import Program from arcade import ArcadeContext - -from .math import rotate_point +from arcade.math import rotate_point __all__ = [ diff --git a/tests/conftest.py b/tests/conftest.py index 7d7bc406b..42366c022 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import os +from contextlib import contextmanager from pathlib import Path import gc @@ -16,7 +17,7 @@ WINDOW = None -def create_window(): +def create_window(width=800, height=600, caption="Testing", **kwargs): global WINDOW if not WINDOW: WINDOW = arcade.Window(title="Testing", vsync=False, antialiasing=False) @@ -90,3 +91,68 @@ def window(): arcade.set_window(window) prepare_window(window) return window + + +# These are tools for integration tests were examples are run +# creating windows and such. The global test window needs to +# be injected somehow. In addition we need to initialize a +# window subclass on an existing window instance. +class WindowTools: + """ + Tools for patching windows in integration tests. + Might cause bleeding from the eyes while reading. + """ + + @contextmanager + def patch_window_as_global_window_subclass(self, window_cls): + """Hack in the class as the subclass of the global window instance""" + # Oh boy here we go .. :O + default_members = None + try: + window = create_window() + arcade.set_window(window) + + # The arcade window initializer must not be called + arcade_init = arcade.Window.__init__ + example_init = window_cls.__init__ + def dummy_arcade_init(self, *args, **kwargs): + arcade.set_window(window) + def dummy_example_init(self, *args, **kwargs): + def dummy_func(self, *args, **kwargs): + pass + window_cls.__init__ = dummy_func + example_init(window, *args, **kwargs) + + arcade.Window.__init__ = dummy_arcade_init + window_cls.__init__ = dummy_example_init + window.__class__ = window_cls # Make subclass of the instance + default_members = set(window.__dict__.keys()) + yield window + finally: + arcade.Window.__init__ = arcade_init + window_cls.__init__ = example_init + window.__class__ = arcade.Window + + # Delete lingering members + if default_members: + new_members = list(window.__dict__.keys()) + for member in new_members: + if member not in default_members: + del window.__dict__[member] + + + +@pytest.fixture(scope="function") +def window_tools(): + """Monkey patch the open_window function and return a WindowTools instance.""" + # Monkey patch the open_window function + def _create_window(width=800, height=800, caption="Test", **kwargs): + window = create_window() + arcade.set_window(window) + prepare_window(window) + window.set_size(width, height) + window.set_caption(caption) + return window + + arcade.open_window = _create_window + return WindowTools() diff --git a/tests/integration/examples/test_examples.py b/tests/integration/examples/test_examples.py new file mode 100644 index 000000000..6e9421f9b --- /dev/null +++ b/tests/integration/examples/test_examples.py @@ -0,0 +1,98 @@ +""" +Import and run all examples one frame +""" +import os +import sys +import importlib +import inspect +from pathlib import Path +import arcade +import pyglet +import pytest + +# TODO: Also add platform_tutorial and gl +EXAMPLE_DIR = Path(arcade.__file__).parent / "examples" +# These examples are allowed to print to stdout +ALLOW_STDOUT = set([ + "arcade.examples.dual_stick_shooter", + "arcade.examples.net_process_animal_facts", +]) + +def list_examples(): + for example in EXAMPLE_DIR.glob("*.py"): + if example.stem.startswith("_"): + continue + print(f"Running {example}") + yield f"arcade.examples.{example.stem}", True + + +def find_class_inheriting_from_window(module): + for name, obj in module.__dict__.items(): + match = inspect.isclass(obj) and issubclass(obj, arcade.Window) + if match: + return obj + return None + + +def find_main_function(module): + if "main" in module.__dict__: + return module.__dict__["main"] + return None + + +@pytest.mark.parametrize( + "module_path, allow_stdout", + list_examples(), +) +def test_examples(window_tools, module_path, allow_stdout): + """Run all examples""" + os.environ["ARCADE_TEST"] = "TRUE" + # Function based example will run on import. + # This is fine because the window_tools fixture patches arcade.open_window + # TODO: Capture stdout from here + module = importlib.import_module(module_path) + # Figure out + # * Is there a class inheriting from arcade.Window? + # * Do we have a main function? + window_cls = find_class_inheriting_from_window(module) + main_func = find_main_function(module) + print(window_cls, main_func) + + if window_cls: + assert main_func, f"Expected a main function in {module_path}" + + # Run the example + with window_tools.patch_window_as_global_window_subclass(window_cls) as window: + main_func() + + +# test_examples() + +# Attempt to inject an existing parent instance + +# window = arcade.Window(800, 600, "My Title") + +# class CustomWindow(arcade.Window): +# def __init__(self): +# super().__init__(800, 600, "My Title") +# self.color = arcade.color.AMAZON + +# def on_draw(self): +# self.clear() +# arcade.draw_text("Hello, world", 200, 300, self.color, 20) + +# def dummy_func(self, *args, **kwargs): +# pass + +# print(window.__dict__) + +# window.__class__ = CustomWindow +# pyglet.window.Window.__init__ = dummy_func +# window.__init__() + + +# arcade.run() + +# print("-" * 100) +# window.__class__ = arcade.Window +# print(window.__dict__) From 094316a0250062fb6b45473cf3a02665e5cf7e0a Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 2 Mar 2024 15:44:55 +0100 Subject: [PATCH 02/10] more tweaks --- tests/conftest.py | 18 +++++++++++------- tests/integration/examples/test_examples.py | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 42366c022..ae16c3703 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -110,23 +110,27 @@ def patch_window_as_global_window_subclass(self, window_cls): default_members = None try: window = create_window() + print("Creating window", type(window), id(window)) arcade.set_window(window) # The arcade window initializer must not be called arcade_init = arcade.Window.__init__ example_init = window_cls.__init__ def dummy_arcade_init(self, *args, **kwargs): + print("Calling dummy arcade init", type(self), id(self)) arcade.set_window(window) - def dummy_example_init(self, *args, **kwargs): + def dummy_example_init(self, *args, **kwargs): + print("Calling dummy example init", type(self), id(self), args, kwargs) def dummy_func(self, *args, **kwargs): pass window_cls.__init__ = dummy_func example_init(window, *args, **kwargs) + # print(window.__dict__) arcade.Window.__init__ = dummy_arcade_init window_cls.__init__ = dummy_example_init window.__class__ = window_cls # Make subclass of the instance - default_members = set(window.__dict__.keys()) + # default_members = set(window.__dict__.keys()) yield window finally: arcade.Window.__init__ = arcade_init @@ -134,11 +138,11 @@ def dummy_func(self, *args, **kwargs): window.__class__ = arcade.Window # Delete lingering members - if default_members: - new_members = list(window.__dict__.keys()) - for member in new_members: - if member not in default_members: - del window.__dict__[member] + # if default_members: + # new_members = list(window.__dict__.keys()) + # for member in new_members: + # if member not in default_members: + # del window.__dict__[member] diff --git a/tests/integration/examples/test_examples.py b/tests/integration/examples/test_examples.py index 6e9421f9b..e46842a58 100644 --- a/tests/integration/examples/test_examples.py +++ b/tests/integration/examples/test_examples.py @@ -56,7 +56,7 @@ def test_examples(window_tools, module_path, allow_stdout): # * Do we have a main function? window_cls = find_class_inheriting_from_window(module) main_func = find_main_function(module) - print(window_cls, main_func) + # print(window_cls, main_func) if window_cls: assert main_func, f"Expected a main function in {module_path}" From b19e823f89fc06b5347e84760356b5fcc5c2f5aa Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 23 Mar 2024 20:05:22 +0100 Subject: [PATCH 03/10] New attempt --- arcade/application.py | 15 +- arcade/examples/performance_statistics.py | 10 +- arcade/window_commands.py | 2 +- tests/conftest.py | 168 +++++++++++------- ..._all_examples.py => _test_all_examples.py} | 0 tests/integration/examples/test_examples.py | 49 ++--- 6 files changed, 132 insertions(+), 112 deletions(-) rename tests/integration/examples/{test_all_examples.py => _test_all_examples.py} (100%) diff --git a/arcade/application.py b/arcade/application.py index 65d8d7596..bc4d8d9b6 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -359,10 +359,7 @@ def center_window(self) -> None: window_width, window_height = self.get_size() # Center the window - try: - self.set_location((screen_width - window_width) // 2, (screen_height - window_height) // 2) - except Exception: - print("moo") + self.set_location((screen_width - window_width) // 2, (screen_height - window_height) // 2) def on_update(self, delta_time: float): """ @@ -741,9 +738,13 @@ def show_view(self, new_view: 'View'): if new_view.window is None: new_view.window = self elif new_view.window != self: - raise RuntimeError("You are attempting to pass the same view " - "object between multiple windows. A single " - "view object can only be used in one window.") + # raise RuntimeError(( + # "You are attempting to pass the same view " + # "object between multiple windows. A single " + # "view object can only be used in one window. " + # f"{self} != {new_view.window}" + # )) + pass # remove previously shown view's handlers if self._current_view is not None: diff --git a/arcade/examples/performance_statistics.py b/arcade/examples/performance_statistics.py index 3e02cbe00..2f19d245c 100644 --- a/arcade/examples/performance_statistics.py +++ b/arcade/examples/performance_statistics.py @@ -41,11 +41,6 @@ COIN_COUNT = 1500 -# Turn on tracking for the number of event handler -# calls and the average execution time of each type. -arcade.enable_timings() - - class Coin(arcade.BasicSprite): """ Our coin sprite class """ def __init__(self, texture: arcade.Texture, scale: float): @@ -187,10 +182,15 @@ def on_key_press(self, symbol: int, modifiers: int): def main(): """ Main function """ + # Turn on tracking for the number of event handler + # calls and the average execution time of each type. + arcade.enable_timings() + window = MyGame() window.setup() arcade.run() + arcade.disable_timings() if __name__ == "__main__": main() diff --git a/arcade/window_commands.py b/arcade/window_commands.py index 85b50a62b..caa62d5f7 100644 --- a/arcade/window_commands.py +++ b/arcade/window_commands.py @@ -173,7 +173,7 @@ def run(): # Used in some unit test if os.environ.get('ARCADE_TEST'): - window.on_update(window._update_rate) + window.on_update(1.0 / 60.0) window.on_draw() elif window.headless: # We are entering headless more an will emulate an event loop diff --git a/tests/conftest.py b/tests/conftest.py index ae16c3703..f652c63c0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,5 @@ import os +import sys from contextlib import contextmanager from pathlib import Path import gc @@ -14,13 +15,14 @@ PROJECT_ROOT = (Path(__file__).parent.parent).resolve() FIXTURE_ROOT = PROJECT_ROOT / "tests" / "fixtures" arcade.resources.add_resource_handle("fixtures", FIXTURE_ROOT) +REAL_WINDOW_CLASS = arcade.Window WINDOW = None def create_window(width=800, height=600, caption="Testing", **kwargs): global WINDOW if not WINDOW: - WINDOW = arcade.Window(title="Testing", vsync=False, antialiasing=False) + WINDOW = REAL_WINDOW_CLASS(title="Testing", vsync=False, antialiasing=False) WINDOW.set_vsync(False) return WINDOW @@ -93,70 +95,112 @@ def window(): return window -# These are tools for integration tests were examples are run -# creating windows and such. The global test window needs to -# be injected somehow. In addition we need to initialize a -# window subclass on an existing window instance. -class WindowTools: - """ - Tools for patching windows in integration tests. - Might cause bleeding from the eyes while reading. - """ +class WindowProxy: + """Fake window extended by integration tests""" + + def __init__(self, width=800, height=600, caption="Test Window", *args, **kwargs): + self.window = create_window() + arcade.set_window(self) + prepare_window(self.window) + if caption: + self.window.set_caption(caption) + if width and height: + self.window.set_size(width, height) + self.window.set_viewport(0, width, 0, height) + + self._update_rate = 60 + + @property + def ctx(self): + return self.window.ctx + + @property + def width(self): + return self.window.width + + @property + def height(self): + return self.window.height + + @property + def size(self): + return self.window.size + + @property + def aspect_ratio(self): + return self.window.aspect_ratio + + def current_view(self): + return self.window.current_view + + @property + def background_color(self): + return self.window.background_color + + @background_color.setter + def background_color(self, color): + self.window.background_color = color + + def clear(self, *args, **kwargs): + return self.window.clear(*args, **kwargs) + + def flip(self): + return self.window.flip() - @contextmanager - def patch_window_as_global_window_subclass(self, window_cls): - """Hack in the class as the subclass of the global window instance""" - # Oh boy here we go .. :O - default_members = None - try: - window = create_window() - print("Creating window", type(window), id(window)) - arcade.set_window(window) - - # The arcade window initializer must not be called - arcade_init = arcade.Window.__init__ - example_init = window_cls.__init__ - def dummy_arcade_init(self, *args, **kwargs): - print("Calling dummy arcade init", type(self), id(self)) - arcade.set_window(window) - def dummy_example_init(self, *args, **kwargs): - print("Calling dummy example init", type(self), id(self), args, kwargs) - def dummy_func(self, *args, **kwargs): - pass - window_cls.__init__ = dummy_func - example_init(window, *args, **kwargs) - # print(window.__dict__) - - arcade.Window.__init__ = dummy_arcade_init - window_cls.__init__ = dummy_example_init - window.__class__ = window_cls # Make subclass of the instance - # default_members = set(window.__dict__.keys()) - yield window - finally: - arcade.Window.__init__ = arcade_init - window_cls.__init__ = example_init - window.__class__ = arcade.Window - - # Delete lingering members - # if default_members: - # new_members = list(window.__dict__.keys()) - # for member in new_members: - # if member not in default_members: - # del window.__dict__[member] + def on_draw(self): + return self.window.on_draw() + + def on_update(self, dt): + return self.window.on_update(dt) + def show_view(self, view): + return self.window.show_view(view) + + def hide_view(self): + return self.window.hide_view() + + def get_size(self): + return self.window.get_size() + + def set_size(self, width, height): + self.window.set_size(width, height) + + def get_pixel_ratio(self): + return self.window.get_pixel_ratio() + + def set_mouse_visible(self, visible): + self.window.set_mouse_visible(visible) + + def center_window(self): + self.window.center_window() + + def set_vsync(self, vsync): + self.window.set_vsync(vsync) + + def get_viewport(self): + return self.window.get_viewport() + + def set_viewport(self, left, right, bottom, top): + self.window.set_viewport(left, right, bottom, top) + + def use(self): + self.window.use() + + def run(self): + self.window.run() @pytest.fixture(scope="function") -def window_tools(): +def window_proxy(): """Monkey patch the open_window function and return a WindowTools instance.""" - # Monkey patch the open_window function - def _create_window(width=800, height=800, caption="Test", **kwargs): - window = create_window() - arcade.set_window(window) - prepare_window(window) - window.set_size(width, height) - window.set_caption(caption) - return window - - arcade.open_window = _create_window - return WindowTools() + _window = arcade.Window + arcade.Window = WindowProxy + + _open_window = arcade.open_window + def open_window(*args, **kwargs): + return create_window(*args, **kwargs) + arcade.open_window = open_window + + yield None + arcade.Window = _window + arcade.open_window = _open_window diff --git a/tests/integration/examples/test_all_examples.py b/tests/integration/examples/_test_all_examples.py similarity index 100% rename from tests/integration/examples/test_all_examples.py rename to tests/integration/examples/_test_all_examples.py diff --git a/tests/integration/examples/test_examples.py b/tests/integration/examples/test_examples.py index e46842a58..1d417b383 100644 --- a/tests/integration/examples/test_examples.py +++ b/tests/integration/examples/test_examples.py @@ -18,11 +18,14 @@ "arcade.examples.net_process_animal_facts", ]) + def list_examples(): for example in EXAMPLE_DIR.glob("*.py"): if example.stem.startswith("_"): continue - print(f"Running {example}") + # print(f"Running {example}") + if "text_loc" in example.stem: + continue yield f"arcade.examples.{example.stem}", True @@ -44,13 +47,19 @@ def find_main_function(module): "module_path, allow_stdout", list_examples(), ) -def test_examples(window_tools, module_path, allow_stdout): + + +def test_examples(window_proxy, module_path, allow_stdout): """Run all examples""" + # full_module_path = f"arcade.examples.{module_path}" + # sys.modules.pop(full_module_path, None) + # assert full_module_path in sys.modules, f"Expected example to not be in sys.modules: {module_path}" os.environ["ARCADE_TEST"] = "TRUE" # Function based example will run on import. # This is fine because the window_tools fixture patches arcade.open_window # TODO: Capture stdout from here module = importlib.import_module(module_path) + # importlib.reload(module) # Figure out # * Is there a class inheriting from arcade.Window? # * Do we have a main function? @@ -60,39 +69,5 @@ def test_examples(window_tools, module_path, allow_stdout): if window_cls: assert main_func, f"Expected a main function in {module_path}" - # Run the example - with window_tools.patch_window_as_global_window_subclass(window_cls) as window: - main_func() - - -# test_examples() - -# Attempt to inject an existing parent instance - -# window = arcade.Window(800, 600, "My Title") - -# class CustomWindow(arcade.Window): -# def __init__(self): -# super().__init__(800, 600, "My Title") -# self.color = arcade.color.AMAZON - -# def on_draw(self): -# self.clear() -# arcade.draw_text("Hello, world", 200, 300, self.color, 20) - -# def dummy_func(self, *args, **kwargs): -# pass - -# print(window.__dict__) - -# window.__class__ = CustomWindow -# pyglet.window.Window.__init__ = dummy_func -# window.__init__() - - -# arcade.run() - -# print("-" * 100) -# window.__class__ = arcade.Window -# print(window.__dict__) + main_func() From c0c85aab8aa6bdbef95e0d557a3178555683a5fd Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 23 Mar 2024 20:07:43 +0100 Subject: [PATCH 04/10] arcade.background_color is not a thing --- arcade/examples/sprite_explosion_bitmapped.py | 2 +- arcade/examples/sprite_move_keyboard_accel.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arcade/examples/sprite_explosion_bitmapped.py b/arcade/examples/sprite_explosion_bitmapped.py index 844953d14..1543fec71 100644 --- a/arcade/examples/sprite_explosion_bitmapped.py +++ b/arcade/examples/sprite_explosion_bitmapped.py @@ -86,7 +86,7 @@ def __init__(self): self.gun_sound = arcade.sound.load_sound(":resources:sounds/laser2.wav") self.hit_sound = arcade.sound.load_sound(":resources:sounds/explosion2.wav") - arcade.background_color = arcade.color.AMAZON + self.background_color = arcade.color.AMAZON def setup(self): diff --git a/arcade/examples/sprite_move_keyboard_accel.py b/arcade/examples/sprite_move_keyboard_accel.py index cd0175d01..5773d55ee 100644 --- a/arcade/examples/sprite_move_keyboard_accel.py +++ b/arcade/examples/sprite_move_keyboard_accel.py @@ -84,7 +84,7 @@ def __init__(self, width, height, title): self.down_pressed = False # Set the background color - arcade.background_color = arcade.color.AMAZON + self.background_color = arcade.color.AMAZON def setup(self): """ Set up the game and initialize the variables. """ From 1d11ef5ee3f57b9d2958e8cd410ed11f8ffe74ab Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 23 Mar 2024 20:14:37 +0100 Subject: [PATCH 05/10] pep8 --- arcade/examples/dual_stick_shooter.py | 2 +- arcade/examples/particle_fireworks.py | 2 +- arcade/examples/particle_systems.py | 2 +- arcade/examples/pymunk_joint_builder.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arcade/examples/dual_stick_shooter.py b/arcade/examples/dual_stick_shooter.py index b966d3126..e34dd1f63 100644 --- a/arcade/examples/dual_stick_shooter.py +++ b/arcade/examples/dual_stick_shooter.py @@ -341,7 +341,7 @@ def on_draw(self): def main(): game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) - arcade.run() + game.run() if __name__ == "__main__": diff --git a/arcade/examples/particle_fireworks.py b/arcade/examples/particle_fireworks.py index 446104868..361e83377 100644 --- a/arcade/examples/particle_fireworks.py +++ b/arcade/examples/particle_fireworks.py @@ -364,7 +364,7 @@ def rocket_smoke_mutator(particle: LifetimeParticle): def main(): app = FireworksApp() - arcade.run() + app.run() if __name__ == "__main__": diff --git a/arcade/examples/particle_systems.py b/arcade/examples/particle_systems.py index 3f937d26a..d1ac99efe 100644 --- a/arcade/examples/particle_systems.py +++ b/arcade/examples/particle_systems.py @@ -768,7 +768,7 @@ def on_key_press(self, key, modifiers): def main(): game = MyGame() - arcade.run() + game.run() if __name__ == "__main__": diff --git a/arcade/examples/pymunk_joint_builder.py b/arcade/examples/pymunk_joint_builder.py index 2720935b9..1548a75a6 100644 --- a/arcade/examples/pymunk_joint_builder.py +++ b/arcade/examples/pymunk_joint_builder.py @@ -316,7 +316,7 @@ def on_update(self, delta_time): def main(): window = MyApplication(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE) - arcade.run() + window.run() if __name__ == "__main__": From 876c1fa4582fba06e94eafafa023cfb6d8dabcec Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 23 Mar 2024 21:13:25 +0100 Subject: [PATCH 06/10] Tweaks --- arcade/application.py | 18 +++---- arcade/examples/tetris.py | 2 +- .../examples => doc}/check_examples_2.py | 0 .../examples => doc}/check_samples.py | 0 ...n_all_examples.py => _run_all_examples.py} | 0 tests/integration/examples/test_examples.py | 50 +++++++++---------- ...all_tutorials.py => _run_all_tutorials.py} | 0 7 files changed, 34 insertions(+), 36 deletions(-) rename tests/{integration/examples => doc}/check_examples_2.py (100%) rename tests/{integration/examples => doc}/check_samples.py (100%) rename tests/integration/examples/{run_all_examples.py => _run_all_examples.py} (100%) rename tests/integration/tutorials/{run_all_tutorials.py => _run_all_tutorials.py} (100%) diff --git a/arcade/application.py b/arcade/application.py index bc4d8d9b6..5d02632b9 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -297,7 +297,7 @@ def background_color(self) -> Color: MY_RED = arcade.types.Color(255, 0, 0) window.background_color = MY_RED - # Set the backgrund color directly from an RGBA tuple + # Set the background color directly from an RGBA tuple window.background_color = 255, 0, 0, 255 # (Discouraged) @@ -474,7 +474,7 @@ def on_mouse_scroll(self, x: int, y: int, scroll_x: int, scroll_y: int): Override this function to respond to scroll events. The scroll arguments may be positive or negative to indicate direction, but - the units are unstandardized. How many scroll steps you recieve + the units are unstandardized. How many scroll steps you receive may vary wildly between computers depending a number of factors, including system settings and the input devices used (i.e. mouse scrollwheel, touchpad, etc). @@ -559,7 +559,7 @@ def on_key_release(self, symbol: int, modifiers: int): Situations that require handling key releases include: - * Rythm games where a note must be held for a certain + * Rhythm games where a note must be held for a certain amount of time * 'Charging up' actions that change strength depending on how long a key was pressed @@ -738,12 +738,12 @@ def show_view(self, new_view: 'View'): if new_view.window is None: new_view.window = self elif new_view.window != self: - # raise RuntimeError(( - # "You are attempting to pass the same view " - # "object between multiple windows. A single " - # "view object can only be used in one window. " - # f"{self} != {new_view.window}" - # )) + raise RuntimeError(( + "You are attempting to pass the same view " + "object between multiple windows. A single " + "view object can only be used in one window. " + f"{self} != {new_view.window}" + )) pass # remove previously shown view's handlers diff --git a/arcade/examples/tetris.py b/arcade/examples/tetris.py index 36158de9e..fde178f70 100644 --- a/arcade/examples/tetris.py +++ b/arcade/examples/tetris.py @@ -201,7 +201,7 @@ def rotate_stone(self): self.stone = new_stone def on_update(self, dt): - """ Update, drop stone if warrented """ + """ Update, drop stone if warranted """ self.frame_count += 1 if self.frame_count % 10 == 0: self.drop() diff --git a/tests/integration/examples/check_examples_2.py b/tests/doc/check_examples_2.py similarity index 100% rename from tests/integration/examples/check_examples_2.py rename to tests/doc/check_examples_2.py diff --git a/tests/integration/examples/check_samples.py b/tests/doc/check_samples.py similarity index 100% rename from tests/integration/examples/check_samples.py rename to tests/doc/check_samples.py diff --git a/tests/integration/examples/run_all_examples.py b/tests/integration/examples/_run_all_examples.py similarity index 100% rename from tests/integration/examples/run_all_examples.py rename to tests/integration/examples/_run_all_examples.py diff --git a/tests/integration/examples/test_examples.py b/tests/integration/examples/test_examples.py index 1d417b383..2cf4e240d 100644 --- a/tests/integration/examples/test_examples.py +++ b/tests/integration/examples/test_examples.py @@ -1,13 +1,15 @@ """ Import and run all examples one frame """ -import os -import sys -import importlib +import contextlib +import io import inspect +import importlib +import os +import time from pathlib import Path + import arcade -import pyglet import pytest # TODO: Also add platform_tutorial and gl @@ -23,9 +25,6 @@ def list_examples(): for example in EXAMPLE_DIR.glob("*.py"): if example.stem.startswith("_"): continue - # print(f"Running {example}") - if "text_loc" in example.stem: - continue yield f"arcade.examples.{example.stem}", True @@ -51,23 +50,22 @@ def find_main_function(module): def test_examples(window_proxy, module_path, allow_stdout): """Run all examples""" - # full_module_path = f"arcade.examples.{module_path}" - # sys.modules.pop(full_module_path, None) - # assert full_module_path in sys.modules, f"Expected example to not be in sys.modules: {module_path}" os.environ["ARCADE_TEST"] = "TRUE" - # Function based example will run on import. - # This is fine because the window_tools fixture patches arcade.open_window - # TODO: Capture stdout from here - module = importlib.import_module(module_path) - # importlib.reload(module) - # Figure out - # * Is there a class inheriting from arcade.Window? - # * Do we have a main function? - window_cls = find_class_inheriting_from_window(module) - main_func = find_main_function(module) - # print(window_cls, main_func) - - if window_cls: - assert main_func, f"Expected a main function in {module_path}" - # Run the example - main_func() + + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + # Function based example will run on import. + # This is fine because the window_tools fixture patches arcade.open_window + module = importlib.import_module(module_path) + + window_cls = find_class_inheriting_from_window(module) + main_func = find_main_function(module) + + if window_cls: + assert main_func, f"Expected a main function in {module_path}" + # Run the example + main_func() + + if not allow_stdout: + output = stdout.getvalue() + assert not output, f"Example {module_path} printed to stdout: {output}" diff --git a/tests/integration/tutorials/run_all_tutorials.py b/tests/integration/tutorials/_run_all_tutorials.py similarity index 100% rename from tests/integration/tutorials/run_all_tutorials.py rename to tests/integration/tutorials/_run_all_tutorials.py From d0418b268ad548d7e112e526386187fa07b5301f Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 23 Mar 2024 21:34:49 +0100 Subject: [PATCH 07/10] More tweaks --- arcade/examples/sections_demo_2.py | 29 ++++++++++++--------- tests/conftest.py | 2 ++ tests/integration/examples/test_examples.py | 4 ++- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/arcade/examples/sections_demo_2.py b/arcade/examples/sections_demo_2.py index c2962f353..5a3494bde 100644 --- a/arcade/examples/sections_demo_2.py +++ b/arcade/examples/sections_demo_2.py @@ -20,8 +20,7 @@ """ import random -from arcade import Window, Section, View, SpriteList, SpriteSolidColor, \ - SpriteCircle, draw_text, draw_line +import arcade from arcade.color import BLACK, BLUE, RED, BEAU_BLUE, GRAY from arcade.key import W, S, UP, DOWN @@ -29,7 +28,7 @@ PLAYER_PADDLE_SPEED = 10 -class Player(Section): +class Player(arcade.Section): """ A Section representing the space in the screen where the player paddle can move @@ -44,7 +43,7 @@ def __init__(self, left: int, bottom: int, width: int, height: int, self.key_down: int = key_down # the player paddle - self.paddle: SpriteSolidColor = SpriteSolidColor(30, 100, color=BLACK) + self.paddle: arcade.SpriteSolidColor = arcade.SpriteSolidColor(30, 100, color=BLACK) # player score self.score: int = 0 @@ -65,10 +64,14 @@ def on_draw(self): else: keys = 'UP and DOWN' start_x = self.left - 290 - draw_text(f'Player {self.name} (move paddle with: {keys})', - start_x, self.top - 20, BLUE, 9) - draw_text(f'Score: {self.score}', self.left + 20, - self.bottom + 20, BLUE) + arcade.draw_text( + f'Player {self.name} (move paddle with: {keys})', + start_x, self.top - 20, BLUE, 9, + ) + arcade.draw_text( + f'Score: {self.score}', self.left + 20, + self.bottom + 20, BLUE, + ) # draw the paddle self.paddle.draw() @@ -85,14 +88,14 @@ def on_key_release(self, _symbol: int, _modifiers: int): self.paddle.stop() -class Pong(View): +class Pong(arcade.View): def __init__(self): super().__init__() # a sprite list that will hold each player paddle to # check for collisions - self.paddles: SpriteList = SpriteList() + self.paddles: arcade.SpriteList = arcade.SpriteList() # we store each Section self.left_player: Player = Player( @@ -111,7 +114,7 @@ def __init__(self): self.paddles.append(self.right_player.paddle) # create the ball - self.ball: SpriteCircle = SpriteCircle(20, RED) + self.ball: arcade.SpriteCircle = arcade.SpriteCircle(20, RED) def setup(self): # set up a new game @@ -168,12 +171,12 @@ def on_draw(self): half_window_x = self.window.width / 2 # middle x # draw a line diving the screen in half - draw_line(half_window_x, 0, half_window_x, self.window.height, GRAY, 2) + arcade.draw_line(half_window_x, 0, half_window_x, self.window.height, GRAY, 2) def main(): # create the window - window = Window(title='Two player simple Pong with Sections!') + window = arcade.Window(title='Two player simple Pong with Sections!') # create the custom View game = Pong() diff --git a/tests/conftest.py b/tests/conftest.py index 7e8938d79..0396fe67a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -148,6 +148,8 @@ def clear(self, *args, **kwargs): return self.window.clear(*args, **kwargs) def flip(self): + if self.window.has_exit: + return return self.window.flip() def on_draw(self): diff --git a/tests/integration/examples/test_examples.py b/tests/integration/examples/test_examples.py index 2cf4e240d..007f92a27 100644 --- a/tests/integration/examples/test_examples.py +++ b/tests/integration/examples/test_examples.py @@ -6,7 +6,7 @@ import inspect import importlib import os -import time +# import time from pathlib import Path import arcade @@ -57,6 +57,7 @@ def test_examples(window_proxy, module_path, allow_stdout): # Function based example will run on import. # This is fine because the window_tools fixture patches arcade.open_window module = importlib.import_module(module_path) + # importlib.reload(module) window_cls = find_class_inheriting_from_window(module) main_func = find_main_function(module) @@ -65,6 +66,7 @@ def test_examples(window_proxy, module_path, allow_stdout): assert main_func, f"Expected a main function in {module_path}" # Run the example main_func() + # time.sleep(1) if not allow_stdout: output = stdout.getvalue() From e75d6fa16ee37ca4ddd723c131d83498b7477cf5 Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sat, 23 Mar 2024 23:59:15 +0100 Subject: [PATCH 08/10] Simplify example tests --- arcade/application.py | 16 +++++----- arcade/gl/buffer.py | 2 +- tests/conftest.py | 6 ++++ tests/integration/examples/test_examples.py | 33 ++++++++------------- 4 files changed, 27 insertions(+), 30 deletions(-) diff --git a/arcade/application.py b/arcade/application.py index 5d02632b9..0da705cb1 100644 --- a/arcade/application.py +++ b/arcade/application.py @@ -737,14 +737,14 @@ def show_view(self, new_view: 'View'): # Store the Window that is showing the "new_view" View. if new_view.window is None: new_view.window = self - elif new_view.window != self: - raise RuntimeError(( - "You are attempting to pass the same view " - "object between multiple windows. A single " - "view object can only be used in one window. " - f"{self} != {new_view.window}" - )) - pass + # NOTE: This is not likely to happen and is creating issues for the test suite. + # elif new_view.window != self: + # raise RuntimeError(( + # "You are attempting to pass the same view " + # "object between multiple windows. A single " + # "view object can only be used in one window. " + # f"{self} != {new_view.window}" + # )) # remove previously shown view's handlers if self._current_view is not None: diff --git a/arcade/gl/buffer.py b/arcade/gl/buffer.py index de7c37d70..6e666197a 100644 --- a/arcade/gl/buffer.py +++ b/arcade/gl/buffer.py @@ -62,7 +62,7 @@ def __init__( gl.glBindBuffer(gl.GL_ARRAY_BUFFER, self._glo) # print(f"glBufferData(gl.GL_ARRAY_BUFFER, {self._size}, data, {self._usage})") - if data is not None and len(data) > 0: + if data is not None and len(data) > 0: # type: ignore self._size, data = data_to_ctypes(data) gl.glBufferData(gl.GL_ARRAY_BUFFER, self._size, data, self._usage) elif reserve > 0: diff --git a/tests/conftest.py b/tests/conftest.py index 0396fe67a..a1048623c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -191,6 +191,12 @@ def set_viewport(self, left, right, bottom, top): def use(self): self.window.use() + def push_handlers(self, *handlers): + self.window.push_handlers(*handlers) + + def remove_handlers(self, *handlers): + self.window.remove_handlers(*handlers) + def run(self): self.window.run() diff --git a/tests/integration/examples/test_examples.py b/tests/integration/examples/test_examples.py index 007f92a27..e81e2f992 100644 --- a/tests/integration/examples/test_examples.py +++ b/tests/integration/examples/test_examples.py @@ -4,9 +4,8 @@ import contextlib import io import inspect -import importlib import os -# import time +from importlib.machinery import SourceFileLoader from pathlib import Path import arcade @@ -19,13 +18,17 @@ "arcade.examples.dual_stick_shooter", "arcade.examples.net_process_animal_facts", ]) - +IGNORE_PATTERNS = [ + 'net_process_animal_facts' +] def list_examples(): for example in EXAMPLE_DIR.glob("*.py"): if example.stem.startswith("_"): continue - yield f"arcade.examples.{example.stem}", True + if example.stem in IGNORE_PATTERNS: + continue + yield f"arcade.examples.{example.stem}", example, True def find_class_inheriting_from_window(module): @@ -43,30 +46,18 @@ def find_main_function(module): @pytest.mark.parametrize( - "module_path, allow_stdout", + "module_path, file_path, allow_stdout", list_examples(), ) - - -def test_examples(window_proxy, module_path, allow_stdout): +def test_examples(window_proxy, module_path, file_path, allow_stdout): """Run all examples""" os.environ["ARCADE_TEST"] = "TRUE" stdout = io.StringIO() with contextlib.redirect_stdout(stdout): - # Function based example will run on import. - # This is fine because the window_tools fixture patches arcade.open_window - module = importlib.import_module(module_path) - # importlib.reload(module) - - window_cls = find_class_inheriting_from_window(module) - main_func = find_main_function(module) - - if window_cls: - assert main_func, f"Expected a main function in {module_path}" - # Run the example - main_func() - # time.sleep(1) + # Manually load the module as __main__ so it runs on import + loader = SourceFileLoader("__main__", str(file_path)) + loader.exec_module(loader.load_module()) if not allow_stdout: output = stdout.getvalue() From ad09550aac5727c2330344f4ea4b13d57e946f02 Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sun, 24 Mar 2024 13:31:11 +0100 Subject: [PATCH 09/10] Test tutorials --- arcade/examples/performance_statistics.py | 9 ++-- tests/conftest.py | 12 +++++ ...ll_tutoirals.py => _test_all_tutoirals.py} | 0 tests/integration/tutorials/test_tutorials.py | 47 +++++++++++++++++++ 4 files changed, 63 insertions(+), 5 deletions(-) rename tests/integration/tutorials/{test_all_tutoirals.py => _test_all_tutoirals.py} (100%) create mode 100644 tests/integration/tutorials/test_tutorials.py diff --git a/arcade/examples/performance_statistics.py b/arcade/examples/performance_statistics.py index 2f19d245c..7b5137be6 100644 --- a/arcade/examples/performance_statistics.py +++ b/arcade/examples/performance_statistics.py @@ -40,6 +40,10 @@ COIN_COUNT = 1500 +# Turn on tracking for the number of event handler +# calls and the average execution time of each type. +arcade.enable_timings() + class Coin(arcade.BasicSprite): """ Our coin sprite class """ @@ -182,15 +186,10 @@ def on_key_press(self, symbol: int, modifiers: int): def main(): """ Main function """ - # Turn on tracking for the number of event handler - # calls and the average execution time of each type. - arcade.enable_timings() - window = MyGame() window.setup() arcade.run() - arcade.disable_timings() if __name__ == "__main__": main() diff --git a/tests/conftest.py b/tests/conftest.py index a1048623c..46b581477 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -41,6 +41,10 @@ def prepare_window(window: arcade.Window): arcade.cleanup_texture_cache() # Clear the global texture cache window.hide_view() # Disable views if any is active window.dispatch_pending_events() + try: + arcade.disable_timings() + except Exception: + pass # Reset context (various states) ctx.reset() @@ -133,6 +137,14 @@ def size(self): def aspect_ratio(self): return self.window.aspect_ratio + @property + def mouse(self): + return self.window.mouse + + @property + def keyboard(self): + return self.window.keyboard + def current_view(self): return self.window.current_view diff --git a/tests/integration/tutorials/test_all_tutoirals.py b/tests/integration/tutorials/_test_all_tutoirals.py similarity index 100% rename from tests/integration/tutorials/test_all_tutoirals.py rename to tests/integration/tutorials/_test_all_tutoirals.py diff --git a/tests/integration/tutorials/test_tutorials.py b/tests/integration/tutorials/test_tutorials.py new file mode 100644 index 000000000..dd179b276 --- /dev/null +++ b/tests/integration/tutorials/test_tutorials.py @@ -0,0 +1,47 @@ +""" +FInd and run all tutorials in the doc/tutorials directory +""" +import io +import os +import contextlib +from importlib.machinery import SourceFileLoader +from pathlib import Path +import pytest +import arcade + +TUTORIAL_DIR = Path(arcade.__file__).parent.parent / "doc" /"tutorials" +ALLOW_STDOUT = {} + + +def find_tutorials(): + # Loop the directory of tutorials dirs + for dir in TUTORIAL_DIR.iterdir(): + if not dir.is_dir(): + continue + + print(dir) + # Find python files in each tutorial dir + for file in dir.glob("*.py"): + if file.stem.startswith("_"): + continue + # print("->", file) + yield file, file.stem in ALLOW_STDOUT + + +@pytest.mark.parametrize( + "file_path, allow_stdout", + find_tutorials(), +) +def test_tutorials(window_proxy, file_path, allow_stdout): + """Run all tutorials""" + os.environ["ARCADE_TEST"] = "TRUE" + stdout = io.StringIO() + with contextlib.redirect_stdout(stdout): + # Manually load the module as __main__ so it runs on import + os.chdir(file_path.parent) + loader = SourceFileLoader("__main__", str(file_path)) + loader.exec_module(loader.load_module()) + + if not allow_stdout: + output = stdout.getvalue() + assert not output, f"Example {file_path} printed to stdout: {output}" From 4cbbb12c6d5e8bb500d9ec47e4c8f57582962111 Mon Sep 17 00:00:00 2001 From: Einar Forselv Date: Sun, 24 Mar 2024 13:35:33 +0100 Subject: [PATCH 10/10] Delete old tests --- .../integration/examples/_run_all_examples.py | 68 ---------------- .../examples/_test_all_examples.py | 78 ------------------- .../tutorials/_run_all_tutorials.py | 67 ---------------- .../tutorials/_test_all_tutoirals.py | 76 ------------------ 4 files changed, 289 deletions(-) delete mode 100644 tests/integration/examples/_run_all_examples.py delete mode 100644 tests/integration/examples/_test_all_examples.py delete mode 100644 tests/integration/tutorials/_run_all_tutorials.py delete mode 100644 tests/integration/tutorials/_test_all_tutoirals.py diff --git a/tests/integration/examples/_run_all_examples.py b/tests/integration/examples/_run_all_examples.py deleted file mode 100644 index 2e293294a..000000000 --- a/tests/integration/examples/_run_all_examples.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -Run All Examples - -If Python and Arcade are installed, this example can be run from the command line with: -python -m tests.test_examples.run_all_examples -""" -import subprocess -import os -import glob - -EXAMPLE_SUBDIR = "../../../arcade/examples" - - -def _get_short_name(fullpath): - return os.path.splitext(os.path.basename(fullpath))[0] - - -def _get_examples(start_path): - query_path = os.path.join(start_path, "*.py") - examples = glob.glob(query_path) - examples = [_get_short_name(e) for e in examples] - examples = [e for e in examples if e != "run_all_examples"] - examples = [e for e in examples if not e.startswith('_')] - examples = ["arcade.examples." + e for e in examples if not e.startswith('_')] - return examples - - -def run_examples(indices_in_range, index_skip_list): - """Run all examples in the arcade/examples directory""" - examples = _get_examples(EXAMPLE_SUBDIR) - examples.sort() - print(f"Found {len(examples)} examples in {EXAMPLE_SUBDIR}") - - file_path = os.path.dirname(os.path.abspath(__file__)) - print(file_path) - os.chdir(file_path+"/../..") - # run examples - for (idx, example) in enumerate(examples): - if indices_in_range is not None and idx not in indices_in_range: - continue - if index_skip_list is not None and idx in index_skip_list: - continue - print(f"=================== Example {idx + 1:3} of {len(examples)}: {example}") - # print('%s %s (index #%d of %d)' % ('=' * 20, example, idx, len(examples) - 1)) - - # Directly call venv, necessary for github action runner - cmd = 'python -m ' + example - - # print(cmd) - result = subprocess.check_output(cmd, shell=True) - if result: - print(f"ERROR: Got a result of: {result}.") - - -def all_examples(): - file_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(file_path) - - # Set an environment variable that will just run on_update() and on_draw() - # once, then quit. - os.environ['ARCADE_TEST'] = "TRUE" - - indices_in_range = None - index_skip_list = None - run_examples(indices_in_range, index_skip_list) - - -all_examples() diff --git a/tests/integration/examples/_test_all_examples.py b/tests/integration/examples/_test_all_examples.py deleted file mode 100644 index 437fec4ec..000000000 --- a/tests/integration/examples/_test_all_examples.py +++ /dev/null @@ -1,78 +0,0 @@ -""" -Run All Examples - -If Python and Arcade are installed, this example can be run from the command line with: -python -m tests.test_examples.test_all_examples -""" -import glob -import os -import subprocess - -import pytest - -EXAMPLE_SUBDIR = "../../../arcade/examples" -# These examples are allowed to print to stdout -ALLOW_STDOUT = set([ - "arcade.examples.dual_stick_shooter", - "arcade.examples.net_process_animal_facts", -]) - -def _get_short_name(fullpath): - return os.path.splitext(os.path.basename(fullpath))[0] - - -def _get_examples(start_path): - query_path = os.path.join(start_path, "*.py") - examples = glob.glob(query_path) - examples = [_get_short_name(e) for e in examples] - examples = [e for e in examples if e != "run_all_examples"] - examples = [e for e in examples if not e.startswith('_')] - examples = ["arcade.examples." + e for e in examples if not e.startswith('_')] - return examples - - -def find_examples(indices_in_range, index_skip_list): - """List all examples in the arcade/examples directory""" - examples = _get_examples(EXAMPLE_SUBDIR) - examples.sort() - print(f"Found {len(examples)} examples in {EXAMPLE_SUBDIR}") - - file_path = os.path.dirname(os.path.abspath(__file__)) - print(file_path) - os.chdir(f"{file_path}/../..") - - for (idx, example) in enumerate(examples): - if indices_in_range is not None and idx not in indices_in_range: - continue - if index_skip_list is not None and idx in index_skip_list: - continue - - allow_stdout = example in ALLOW_STDOUT - yield f'python -m {example}', allow_stdout - - -def list_examples(indices_in_range, index_skip_list): - file_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(file_path) - - return list(find_examples(indices_in_range, index_skip_list)) - - -@pytest.mark.parametrize( - "cmd, allow_stdout", - argvalues=list_examples( - indices_in_range=None, - index_skip_list=None - ) -) -def test_all(cmd, allow_stdout): - # Set an environment variable that will just run on_update() and on_draw() - # once, then quit. - import pyglet - test_env = os.environ.copy() - test_env["ARCADE_TEST"] = "TRUE" - - result = subprocess.check_output(cmd, shell=True, env=test_env) - if result and not allow_stdout: - print(f"ERROR: Got a result of: {result}.") - assert not result diff --git a/tests/integration/tutorials/_run_all_tutorials.py b/tests/integration/tutorials/_run_all_tutorials.py deleted file mode 100644 index 3abd1c36f..000000000 --- a/tests/integration/tutorials/_run_all_tutorials.py +++ /dev/null @@ -1,67 +0,0 @@ -""" -Run All tutorials - -If Python and Arcade are installed, this tutorial can be run from the command line with: -python -m tests.test_tutorials.run_all_tutorials -""" -import glob -import subprocess -import os -from pathlib import Path - -TUTORIAL_SUBDIR = "../../../doc/tutorials/" - - -def _get_short_name(fullpath): - return os.path.splitext(os.path.basename(fullpath))[0] - -def _get_tutorials(start_path): - query_path = os.path.join(start_path, "*.py") - tutorials = glob.glob(query_path) - tutorials = [_get_short_name(e) for e in tutorials] - tutorials = [e for e in tutorials if e != "run_all_tutorials"] - tutorials = [e for e in tutorials if not e.startswith('_')] - tutorials = [f"doc.tutorials.{start_path.name}." + e for e in tutorials if not e.startswith('_')] - return tutorials - -def run_tutorials(indices_in_range = None, index_skip_list = None): - """Run all tutorials in the doc/tutorials directory""" - for tutorial_subdir in [path for path in list(Path.cwd().joinpath(TUTORIAL_SUBDIR).iterdir()) if path.is_dir()]: - tutorials = _get_tutorials(tutorial_subdir) - tutorials.sort() - print(f"Found {len(tutorials)} tutorials in {tutorial_subdir}") - - file_path = os.path.dirname(os.path.abspath(__file__)) - print(file_path) - os.chdir(file_path+"/../..") - # run tutorials - for (idx, tutorial) in enumerate(tutorials): - if indices_in_range is not None and idx not in indices_in_range: - continue - if index_skip_list is not None and idx in index_skip_list: - continue - print(f"=================== tutorial {idx + 1:3} of {len(tutorials)}: {tutorial}") - # print('%s %s (index #%d of %d)' % ('=' * 20, tutorial, idx, len(tutorials) - 1)) - - # Directly call venv, necessary for github action runner - cmd = 'python -m ' + tutorial - - # print(cmd) - result = subprocess.check_output(cmd, shell=True) - if result: - print(f"ERROR: Got a result of: {result}.") - -def all_tutorials(): - file_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(file_path) - - # Set an environment variable that will just run on_update() and on_draw() - # once, then quit. - os.environ['ARCADE_TEST'] = "TRUE" - - indices_in_range = None - index_skip_list = None - run_tutorials(indices_in_range, index_skip_list) - - -all_tutorials() diff --git a/tests/integration/tutorials/_test_all_tutoirals.py b/tests/integration/tutorials/_test_all_tutoirals.py deleted file mode 100644 index aeb94419b..000000000 --- a/tests/integration/tutorials/_test_all_tutoirals.py +++ /dev/null @@ -1,76 +0,0 @@ -""" -Run All tutorials - -If Python and Arcade are installed, this tutorial can be run from the command line with: -python -m tests.test_tutorials.test_all_tutorials -""" -import glob -import os -import subprocess -from pathlib import Path - -import pytest - -TUTORIAL_SUBDIR = "../../../doc/tutorials/" -# These tutorials are allowed to print to stdout -ALLOW_STDOUT = set() - -def _get_short_name(fullpath): - return os.path.splitext(os.path.basename(fullpath))[0] - -def _get_tutorials(start_path): - query_path = os.path.join(start_path, "*.py") - tutorials = glob.glob(query_path) - tutorials = [_get_short_name(e) for e in tutorials] - tutorials = [e for e in tutorials if e != "run_all_tutorials"] - tutorials = [e for e in tutorials if not e.startswith('_')] - tutorials = [f"{e}.py" for e in tutorials if not e.startswith('_')] - return tutorials - -def find_tutorials(indices_in_range, index_skip_list): - """List all tutorials in the doc/tutorials directory""" - file_dir = Path(__file__).parent - for tutorial_subdir in [path for path in list((file_dir / TUTORIAL_SUBDIR).iterdir()) if path.is_dir()]: - tutorials = _get_tutorials(tutorial_subdir) - tutorials.sort() - print(f"Found {len(tutorials)} tutorials in {tutorial_subdir}") - if len(tutorials) == 0: - continue - print(tutorial_subdir) - # os.chdir(tutorial_subdir) - for (idx, tutorial) in enumerate(tutorials): - if indices_in_range is not None and idx not in indices_in_range: - continue - if index_skip_list is not None and idx in index_skip_list: - continue - - allow_stdout = tutorial in ALLOW_STDOUT - yield f'python {tutorial}', allow_stdout, tutorial_subdir - # os.chdir("../") - - -def list_tutorials(indices_in_range, index_skip_list): - file_path = os.path.dirname(os.path.abspath(__file__)) - os.chdir(file_path) - - return list(find_tutorials(indices_in_range, index_skip_list)) - - -@pytest.mark.parametrize( - "cmd, allow_stdout, tutorial_subdir", - argvalues=list_tutorials( - indices_in_range=None, - index_skip_list=None - ) -) -def test_all(cmd, allow_stdout, tutorial_subdir): - # Set an environment variable that will just run on_update() and on_draw() - # once, then quit. - import pyglet - test_env = os.environ.copy() - test_env["ARCADE_TEST"] = "TRUE" - os.chdir(tutorial_subdir) - result = subprocess.check_output(cmd, shell=True, env=test_env) - if result and not allow_stdout: - print(f"ERROR: Got a result of: {result}.") - assert not result