From 6b038367a63bc83b1955df8e631f3e2cccdc7978 Mon Sep 17 00:00:00 2001
From: Fenysha <126053988+TeshariEnjoer@users.noreply.github.com>
Date: Sat, 10 Aug 2024 02:33:04 +0300
Subject: [PATCH] Abstract map: - Self-madet HTTP server - Multi-threding
pygame execution
---
laplas/abstract_map/objects/camera.py | 46 +
.../abstract_map/objects/object.py | 0
laplas/{tools => }/abstract_map/overmap.py | 21 +-
.../pygame-2.6.0.dist-info/INSTALLER | 1 +
.../pygame-2.6.0.dist-info/METADATA | 314 +
.../pygame-2.6.0.dist-info/RECORD | 771 ++
.../pygame-2.6.0.dist-info/REQUESTED} | 0
.../abstract_map/pygame-2.6.0.dist-info/WHEEL | 5 +
.../pygame-2.6.0.dist-info/entry_points.txt | 3 +
.../pygame-2.6.0.dist-info/top_level.txt | 1 +
.../{tools => }/abstract_map/pygame/SDL2.dll | Bin
.../abstract_map/pygame/SDL2_image.dll | Bin
.../abstract_map/pygame/SDL2_mixer.dll | Bin
.../abstract_map/pygame/SDL2_ttf.dll | Bin
.../abstract_map/pygame/__init__.py | 0
.../abstract_map/pygame/__init__.pyi | 0
.../pygame/__pyinstaller/__init__.py | 0
.../pygame/__pyinstaller/hook-pygame.py | 0
.../abstract_map/pygame/_camera_opencv.py | 0
.../abstract_map/pygame/_camera_vidcapture.py | 0
.../abstract_map/pygame/_common.pyi | 0
.../abstract_map/pygame/_sdl2/__init__.py | 0
.../abstract_map/pygame/_sdl2/__init__.pyi | 0
.../abstract_map/pygame/_sdl2/audio.pyi | 0
.../abstract_map/pygame/_sdl2/controller.pyi | 0
.../abstract_map/pygame/_sdl2/sdl2.pyi | 0
.../abstract_map/pygame/_sdl2/touch.pyi | 0
.../abstract_map/pygame/_sdl2/video.pyi | 0
.../{tools => }/abstract_map/pygame/base.pyi | 0
.../abstract_map/pygame/bufferproxy.pyi | 0
.../{tools => }/abstract_map/pygame/camera.py | 0
.../abstract_map/pygame/camera.pyi | 0
.../{tools => }/abstract_map/pygame/color.pyi | 0
.../abstract_map/pygame/colordict.py | 0
.../abstract_map/pygame/constants.pyi | 0
.../abstract_map/pygame/cursors.py | 0
.../abstract_map/pygame/cursors.pyi | 0
.../abstract_map/pygame/display.pyi | 0
.../{tools => }/abstract_map/pygame/draw.pyi | 0
.../abstract_map/pygame/draw_py.py | 0
.../{tools => }/abstract_map/pygame/event.pyi | 0
.../abstract_map/pygame/fastevent.py | 0
.../abstract_map/pygame/fastevent.pyi | 0
.../{tools => }/abstract_map/pygame/font.pyi | 0
.../abstract_map/pygame/freesansbold.ttf | Bin
.../abstract_map/pygame/freetype.dll | Bin
.../abstract_map/pygame/freetype.py | 0
.../abstract_map/pygame/freetype.pyi | 0
.../{tools => }/abstract_map/pygame/ftfont.py | 0
.../abstract_map/pygame/gfxdraw.pyi | 0
.../{tools => }/abstract_map/pygame/image.pyi | 0
.../abstract_map/pygame/joystick.pyi | 0
.../{tools => }/abstract_map/pygame/key.pyi | 0
.../abstract_map/pygame/libjpeg-9.dll | Bin
.../abstract_map/pygame/libmodplug-1.dll | Bin
.../abstract_map/pygame/libogg-0.dll | Bin
.../abstract_map/pygame/libopus-0.dll | Bin
.../abstract_map/pygame/libopusfile-0.dll | Bin
.../abstract_map/pygame/libpng16-16.dll | Bin
.../abstract_map/pygame/libtiff-5.dll | Bin
.../abstract_map/pygame/libwebp-7.dll | Bin
.../{tools => }/abstract_map/pygame/locals.py | 0
.../abstract_map/pygame/locals.pyi | 0
.../{tools => }/abstract_map/pygame/macosx.py | 0
.../{tools => }/abstract_map/pygame/mask.pyi | 0
.../{tools => }/abstract_map/pygame/math.pyi | 0
.../{tools => }/abstract_map/pygame/midi.py | 0
.../{tools => }/abstract_map/pygame/midi.pyi | 0
.../{tools => }/abstract_map/pygame/mixer.pyi | 0
.../abstract_map/pygame/mixer_music.pyi | 0
.../{tools => }/abstract_map/pygame/mouse.pyi | 0
.../abstract_map/pygame/pixelarray.pyi | 0
.../abstract_map/pygame/pixelcopy.pyi | 0
.../abstract_map/pygame/pkgdata.py | 0
.../abstract_map/pygame/portmidi.dll | Bin
.../{tools => }/abstract_map/pygame/py.typed | 0
.../abstract_map/pygame/pygame.ico | Bin
.../abstract_map/pygame/pygame_icon.bmp | Bin
.../abstract_map/pygame/pygame_icon.icns | Bin
.../abstract_map/pygame/pygame_icon_mac.bmp | Bin
.../{tools => }/abstract_map/pygame/rect.pyi | 0
.../abstract_map/pygame/rwobject.pyi | 0
.../{tools => }/abstract_map/pygame/scrap.pyi | 0
.../abstract_map/pygame/sndarray.py | 0
.../abstract_map/pygame/sndarray.pyi | 0
.../{tools => }/abstract_map/pygame/sprite.py | 0
.../abstract_map/pygame/sprite.pyi | 0
.../abstract_map/pygame/surface.pyi | 0
.../abstract_map/pygame/surfarray.py | 0
.../abstract_map/pygame/surfarray.pyi | 0
.../abstract_map/pygame/surflock.pyi | 0
.../abstract_map/pygame/sysfont.py | 0
laplas/abstract_map/pygame/tests/__init__.py | 40 +
laplas/abstract_map/pygame/tests/__main__.py | 143 +
laplas/abstract_map/pygame/tests/base_test.py | 623 ++
laplas/abstract_map/pygame/tests/blit_test.py | 192 +
.../pygame/tests/bufferproxy_test.py | 504 ++
.../abstract_map/pygame/tests/camera_test.py | 35 +
.../abstract_map/pygame/tests/color_test.py | 1360 ++++
.../pygame/tests/constants_test.py | 426 ++
.../pygame/tests/controller_test.py | 357 +
.../abstract_map/pygame/tests/cursors_test.py | 290 +
.../abstract_map/pygame/tests/display_test.py | 1199 +++
laplas/abstract_map/pygame/tests/docs_test.py | 35 +
laplas/abstract_map/pygame/tests/draw_test.py | 6577 +++++++++++++++++
.../abstract_map/pygame/tests/event_test.py | 961 +++
.../tests/fixtures/fonts/A_PyGameMono-8.png | Bin 0 -> 92 bytes
.../fonts/PlayfairDisplaySemibold.ttf | Bin 0 -> 236636 bytes
.../fixtures/fonts/PyGameMono-18-100dpi.bdf | 165 +
.../fixtures/fonts/PyGameMono-18-75dpi.bdf | 143 +
.../tests/fixtures/fonts/PyGameMono-8.bdf | 103 +
.../tests/fixtures/fonts/PyGameMono.otf | Bin 0 -> 3128 bytes
.../tests/fixtures/fonts/test_fixed.otf | Bin 0 -> 58464 bytes
.../pygame/tests/fixtures/fonts/test_sans.ttf | Bin 0 -> 133088 bytes
.../fixtures/fonts/u13079_PyGameMono-8.png | Bin 0 -> 89 bytes
.../fixtures/xbm_cursors/white_sizing.xbm | 8 +
.../xbm_cursors/white_sizing_mask.xbm | 8 +
laplas/abstract_map/pygame/tests/font_test.py | 748 ++
.../pygame/tests/freetype_tags.py | 11 +
.../pygame/tests/freetype_test.py | 1796 +++++
.../abstract_map/pygame/tests/ftfont_tags.py | 11 +
.../abstract_map/pygame/tests/ftfont_test.py | 17 +
.../abstract_map/pygame/tests/gfxdraw_test.py | 876 +++
.../tests/image__save_gl_surface_test.py | 46 +
.../abstract_map/pygame/tests/image_tags.py | 7 +
.../abstract_map/pygame/tests/image_test.py | 1282 ++++
.../pygame/tests/imageext_tags.py | 7 +
.../pygame/tests/imageext_test.py | 93 +
.../pygame/tests/joystick_test.py | 166 +
laplas/abstract_map/pygame/tests/key_test.py | 306 +
.../abstract_map/pygame/tests/locals_test.py | 17 +
laplas/abstract_map/pygame/tests/mask_test.py | 6441 ++++++++++++++++
laplas/abstract_map/pygame/tests/math_test.py | 2929 ++++++++
laplas/abstract_map/pygame/tests/midi_test.py | 463 ++
.../pygame/tests/mixer_music_tags.py | 7 +
.../pygame/tests/mixer_music_test.py | 493 ++
.../abstract_map/pygame/tests/mixer_tags.py | 7 +
.../abstract_map/pygame/tests/mixer_test.py | 1479 ++++
.../abstract_map/pygame/tests/mouse_test.py | 348 +
.../pygame/tests/pixelarray_test.py | 1667 +++++
.../pygame/tests/pixelcopy_test.py | 710 ++
laplas/abstract_map/pygame/tests/rect_test.py | 3312 +++++++++
.../pygame/tests/run_tests__tests/__init__.py | 1 +
.../tests/run_tests__tests/all_ok/__init__.py | 1 +
.../run_tests__tests/all_ok/fake_2_test.py | 39 +
.../run_tests__tests/all_ok/fake_3_test.py | 39 +
.../run_tests__tests/all_ok/fake_4_test.py | 39 +
.../run_tests__tests/all_ok/fake_5_test.py | 39 +
.../run_tests__tests/all_ok/fake_6_test.py | 39 +
.../no_assertions__ret_code_of_1__test.py | 39 +
.../all_ok/zero_tests_test.py | 23 +
.../run_tests__tests/everything/__init__.py | 1 +
.../everything/fake_2_test.py | 39 +
.../everything/incomplete_todo_test.py | 39 +
.../everything/magic_tag_test.py | 38 +
.../run_tests__tests/everything/sleep_test.py | 29 +
.../run_tests__tests/exclude/__init__.py | 1 +
.../run_tests__tests/exclude/fake_2_test.py | 39 +
.../exclude/invisible_tag_test.py | 41 +
.../exclude/magic_tag_test.py | 38 +
.../run_tests__tests/failures1/__init__.py | 1 +
.../run_tests__tests/failures1/fake_2_test.py | 39 +
.../run_tests__tests/failures1/fake_3_test.py | 39 +
.../run_tests__tests/failures1/fake_4_test.py | 41 +
.../run_tests__tests/incomplete/__init__.py | 1 +
.../incomplete/fake_2_test.py | 39 +
.../incomplete/fake_3_test.py | 39 +
.../incomplete_todo/__init__.py | 1 +
.../incomplete_todo/fake_2_test.py | 39 +
.../incomplete_todo/fake_3_test.py | 39 +
.../infinite_loop/__init__.py | 1 +
.../infinite_loop/fake_1_test.py | 40 +
.../infinite_loop/fake_2_test.py | 39 +
.../run_tests__tests/print_stderr/__init__.py | 1 +
.../print_stderr/fake_2_test.py | 39 +
.../print_stderr/fake_3_test.py | 41 +
.../print_stderr/fake_4_test.py | 41 +
.../run_tests__tests/print_stdout/__init__.py | 1 +
.../print_stdout/fake_2_test.py | 39 +
.../print_stdout/fake_3_test.py | 42 +
.../print_stdout/fake_4_test.py | 41 +
.../tests/run_tests__tests/run_tests__test.py | 145 +
.../run_tests__tests/timeout/__init__.py | 1 +
.../run_tests__tests/timeout/fake_2_test.py | 39 +
.../run_tests__tests/timeout/sleep_test.py | 30 +
.../pygame/tests/rwobject_test.py | 139 +
.../abstract_map/pygame/tests/scrap_tags.py | 26 +
.../abstract_map/pygame/tests/scrap_test.py | 301 +
.../pygame/tests/sndarray_tags.py | 12 +
.../pygame/tests/sndarray_test.py | 154 +
.../abstract_map/pygame/tests/sprite_test.py | 1412 ++++
.../abstract_map/pygame/tests/surface_test.py | 4064 ++++++++++
.../pygame/tests/surfarray_tags.py | 16 +
.../pygame/tests/surfarray_test.py | 743 ++
.../pygame/tests/surflock_test.py | 144 +
.../abstract_map/pygame/tests/sysfont_test.py | 51 +
.../pygame/tests/test_utils/__init__.py | 201 +
.../pygame/tests/test_utils/arrinter.py | 438 ++
.../pygame/tests/test_utils/async_sub.py | 301 +
.../pygame/tests/test_utils/buftools.py | 607 ++
.../pygame/tests/test_utils/endian.py | 20 +
.../pygame/tests/test_utils/png.py | 4005 ++++++++++
.../pygame/tests/test_utils/run_tests.py | 350 +
.../pygame/tests/test_utils/test_machinery.py | 89 +
.../pygame/tests/test_utils/test_runner.py | 324 +
.../abstract_map/pygame/tests/threads_test.py | 238 +
laplas/abstract_map/pygame/tests/time_test.py | 410 +
.../abstract_map/pygame/tests/touch_test.py | 97 +
.../pygame/tests/transform_test.py | 1420 ++++
.../abstract_map/pygame/tests/version_test.py | 48 +
.../abstract_map/pygame/tests/video_test.py | 26 +
.../abstract_map/pygame/threads/__init__.py | 0
.../{tools => }/abstract_map/pygame/time.pyi | 0
.../abstract_map/pygame/transform.pyi | 0
.../abstract_map/pygame/version.py | 0
.../abstract_map/pygame/version.pyi | 0
.../{tools => }/abstract_map/pygame/zlib1.dll | Bin
laplas/abstract_map/server.py | 146 +
.../abstract_overmap/SSabstract_overmap.dm | 1 -
laplas/tools/abstract_map/camera.py | 40 -
laplas/tools/abstract_map/flask/__init__.py | 46 -
laplas/tools/abstract_map/flask/__main__.py | 3 -
laplas/tools/abstract_map/flask/app.py | 2091 ------
laplas/tools/abstract_map/flask/blueprints.py | 609 --
laplas/tools/abstract_map/flask/cli.py | 999 ---
laplas/tools/abstract_map/flask/config.py | 295 -
laplas/tools/abstract_map/flask/ctx.py | 489 --
.../tools/abstract_map/flask/debughelpers.py | 172 -
laplas/tools/abstract_map/flask/globals.py | 59 -
laplas/tools/abstract_map/flask/helpers.py | 836 ---
.../tools/abstract_map/flask/json/__init__.py | 363 -
laplas/tools/abstract_map/flask/json/tag.py | 312 -
laplas/tools/abstract_map/flask/logging.py | 74 -
laplas/tools/abstract_map/flask/scaffold.py | 875 ---
laplas/tools/abstract_map/flask/sessions.py | 416 --
laplas/tools/abstract_map/flask/signals.py | 56 -
laplas/tools/abstract_map/flask/templating.py | 165 -
laplas/tools/abstract_map/flask/testing.py | 298 -
laplas/tools/abstract_map/flask/typing.py | 49 -
laplas/tools/abstract_map/flask/views.py | 158 -
laplas/tools/abstract_map/flask/wrappers.py | 167 -
laplas/tools/abstract_map/main.py | 92 -
242 files changed, 54687 insertions(+), 8672 deletions(-)
create mode 100644 laplas/abstract_map/objects/camera.py
rename laplas/{tools => }/abstract_map/objects/object.py (100%)
rename laplas/{tools => }/abstract_map/overmap.py (74%)
create mode 100644 laplas/abstract_map/pygame-2.6.0.dist-info/INSTALLER
create mode 100644 laplas/abstract_map/pygame-2.6.0.dist-info/METADATA
create mode 100644 laplas/abstract_map/pygame-2.6.0.dist-info/RECORD
rename laplas/{tools/abstract_map/flask/py.typed => abstract_map/pygame-2.6.0.dist-info/REQUESTED} (100%)
create mode 100644 laplas/abstract_map/pygame-2.6.0.dist-info/WHEEL
create mode 100644 laplas/abstract_map/pygame-2.6.0.dist-info/entry_points.txt
create mode 100644 laplas/abstract_map/pygame-2.6.0.dist-info/top_level.txt
rename laplas/{tools => }/abstract_map/pygame/SDL2.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/SDL2_image.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/SDL2_mixer.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/SDL2_ttf.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/__init__.py (100%)
rename laplas/{tools => }/abstract_map/pygame/__init__.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/__pyinstaller/__init__.py (100%)
rename laplas/{tools => }/abstract_map/pygame/__pyinstaller/hook-pygame.py (100%)
rename laplas/{tools => }/abstract_map/pygame/_camera_opencv.py (100%)
rename laplas/{tools => }/abstract_map/pygame/_camera_vidcapture.py (100%)
rename laplas/{tools => }/abstract_map/pygame/_common.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/_sdl2/__init__.py (100%)
rename laplas/{tools => }/abstract_map/pygame/_sdl2/__init__.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/_sdl2/audio.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/_sdl2/controller.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/_sdl2/sdl2.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/_sdl2/touch.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/_sdl2/video.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/base.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/bufferproxy.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/camera.py (100%)
rename laplas/{tools => }/abstract_map/pygame/camera.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/color.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/colordict.py (100%)
rename laplas/{tools => }/abstract_map/pygame/constants.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/cursors.py (100%)
rename laplas/{tools => }/abstract_map/pygame/cursors.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/display.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/draw.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/draw_py.py (100%)
rename laplas/{tools => }/abstract_map/pygame/event.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/fastevent.py (100%)
rename laplas/{tools => }/abstract_map/pygame/fastevent.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/font.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/freesansbold.ttf (100%)
rename laplas/{tools => }/abstract_map/pygame/freetype.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/freetype.py (100%)
rename laplas/{tools => }/abstract_map/pygame/freetype.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/ftfont.py (100%)
rename laplas/{tools => }/abstract_map/pygame/gfxdraw.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/image.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/joystick.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/key.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/libjpeg-9.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/libmodplug-1.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/libogg-0.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/libopus-0.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/libopusfile-0.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/libpng16-16.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/libtiff-5.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/libwebp-7.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/locals.py (100%)
rename laplas/{tools => }/abstract_map/pygame/locals.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/macosx.py (100%)
rename laplas/{tools => }/abstract_map/pygame/mask.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/math.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/midi.py (100%)
rename laplas/{tools => }/abstract_map/pygame/midi.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/mixer.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/mixer_music.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/mouse.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/pixelarray.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/pixelcopy.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/pkgdata.py (100%)
rename laplas/{tools => }/abstract_map/pygame/portmidi.dll (100%)
rename laplas/{tools => }/abstract_map/pygame/py.typed (100%)
rename laplas/{tools => }/abstract_map/pygame/pygame.ico (100%)
rename laplas/{tools => }/abstract_map/pygame/pygame_icon.bmp (100%)
rename laplas/{tools => }/abstract_map/pygame/pygame_icon.icns (100%)
rename laplas/{tools => }/abstract_map/pygame/pygame_icon_mac.bmp (100%)
rename laplas/{tools => }/abstract_map/pygame/rect.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/rwobject.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/scrap.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/sndarray.py (100%)
rename laplas/{tools => }/abstract_map/pygame/sndarray.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/sprite.py (100%)
rename laplas/{tools => }/abstract_map/pygame/sprite.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/surface.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/surfarray.py (100%)
rename laplas/{tools => }/abstract_map/pygame/surfarray.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/surflock.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/sysfont.py (100%)
create mode 100644 laplas/abstract_map/pygame/tests/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/__main__.py
create mode 100644 laplas/abstract_map/pygame/tests/base_test.py
create mode 100644 laplas/abstract_map/pygame/tests/blit_test.py
create mode 100644 laplas/abstract_map/pygame/tests/bufferproxy_test.py
create mode 100644 laplas/abstract_map/pygame/tests/camera_test.py
create mode 100644 laplas/abstract_map/pygame/tests/color_test.py
create mode 100644 laplas/abstract_map/pygame/tests/constants_test.py
create mode 100644 laplas/abstract_map/pygame/tests/controller_test.py
create mode 100644 laplas/abstract_map/pygame/tests/cursors_test.py
create mode 100644 laplas/abstract_map/pygame/tests/display_test.py
create mode 100644 laplas/abstract_map/pygame/tests/docs_test.py
create mode 100644 laplas/abstract_map/pygame/tests/draw_test.py
create mode 100644 laplas/abstract_map/pygame/tests/event_test.py
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/A_PyGameMono-8.png
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/PlayfairDisplaySemibold.ttf
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/PyGameMono-8.bdf
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/PyGameMono.otf
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/test_fixed.otf
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/test_sans.ttf
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/xbm_cursors/white_sizing.xbm
create mode 100644 laplas/abstract_map/pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm
create mode 100644 laplas/abstract_map/pygame/tests/font_test.py
create mode 100644 laplas/abstract_map/pygame/tests/freetype_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/freetype_test.py
create mode 100644 laplas/abstract_map/pygame/tests/ftfont_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/ftfont_test.py
create mode 100644 laplas/abstract_map/pygame/tests/gfxdraw_test.py
create mode 100644 laplas/abstract_map/pygame/tests/image__save_gl_surface_test.py
create mode 100644 laplas/abstract_map/pygame/tests/image_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/image_test.py
create mode 100644 laplas/abstract_map/pygame/tests/imageext_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/imageext_test.py
create mode 100644 laplas/abstract_map/pygame/tests/joystick_test.py
create mode 100644 laplas/abstract_map/pygame/tests/key_test.py
create mode 100644 laplas/abstract_map/pygame/tests/locals_test.py
create mode 100644 laplas/abstract_map/pygame/tests/mask_test.py
create mode 100644 laplas/abstract_map/pygame/tests/math_test.py
create mode 100644 laplas/abstract_map/pygame/tests/midi_test.py
create mode 100644 laplas/abstract_map/pygame/tests/mixer_music_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/mixer_music_test.py
create mode 100644 laplas/abstract_map/pygame/tests/mixer_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/mixer_test.py
create mode 100644 laplas/abstract_map/pygame/tests/mouse_test.py
create mode 100644 laplas/abstract_map/pygame/tests/pixelarray_test.py
create mode 100644 laplas/abstract_map/pygame/tests/pixelcopy_test.py
create mode 100644 laplas/abstract_map/pygame/tests/rect_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/fake_3_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/fake_4_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/fake_5_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/fake_6_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/all_ok/zero_tests_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/everything/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/everything/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/everything/incomplete_todo_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/everything/magic_tag_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/everything/sleep_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/exclude/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/exclude/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/exclude/invisible_tag_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/exclude/magic_tag_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/failures1/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/failures1/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/failures1/fake_3_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/failures1/fake_4_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/incomplete/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/incomplete/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/incomplete/fake_3_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/incomplete_todo/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/infinite_loop/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stderr/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stderr/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stderr/fake_3_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stderr/fake_4_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stdout/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stdout/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stdout/fake_3_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/print_stdout/fake_4_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/run_tests__test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/timeout/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/timeout/fake_2_test.py
create mode 100644 laplas/abstract_map/pygame/tests/run_tests__tests/timeout/sleep_test.py
create mode 100644 laplas/abstract_map/pygame/tests/rwobject_test.py
create mode 100644 laplas/abstract_map/pygame/tests/scrap_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/scrap_test.py
create mode 100644 laplas/abstract_map/pygame/tests/sndarray_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/sndarray_test.py
create mode 100644 laplas/abstract_map/pygame/tests/sprite_test.py
create mode 100644 laplas/abstract_map/pygame/tests/surface_test.py
create mode 100644 laplas/abstract_map/pygame/tests/surfarray_tags.py
create mode 100644 laplas/abstract_map/pygame/tests/surfarray_test.py
create mode 100644 laplas/abstract_map/pygame/tests/surflock_test.py
create mode 100644 laplas/abstract_map/pygame/tests/sysfont_test.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/__init__.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/arrinter.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/async_sub.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/buftools.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/endian.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/png.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/run_tests.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/test_machinery.py
create mode 100644 laplas/abstract_map/pygame/tests/test_utils/test_runner.py
create mode 100644 laplas/abstract_map/pygame/tests/threads_test.py
create mode 100644 laplas/abstract_map/pygame/tests/time_test.py
create mode 100644 laplas/abstract_map/pygame/tests/touch_test.py
create mode 100644 laplas/abstract_map/pygame/tests/transform_test.py
create mode 100644 laplas/abstract_map/pygame/tests/version_test.py
create mode 100644 laplas/abstract_map/pygame/tests/video_test.py
rename laplas/{tools => }/abstract_map/pygame/threads/__init__.py (100%)
rename laplas/{tools => }/abstract_map/pygame/time.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/transform.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/version.py (100%)
rename laplas/{tools => }/abstract_map/pygame/version.pyi (100%)
rename laplas/{tools => }/abstract_map/pygame/zlib1.dll (100%)
create mode 100644 laplas/abstract_map/server.py
delete mode 100644 laplas/tools/abstract_map/camera.py
delete mode 100644 laplas/tools/abstract_map/flask/__init__.py
delete mode 100644 laplas/tools/abstract_map/flask/__main__.py
delete mode 100644 laplas/tools/abstract_map/flask/app.py
delete mode 100644 laplas/tools/abstract_map/flask/blueprints.py
delete mode 100644 laplas/tools/abstract_map/flask/cli.py
delete mode 100644 laplas/tools/abstract_map/flask/config.py
delete mode 100644 laplas/tools/abstract_map/flask/ctx.py
delete mode 100644 laplas/tools/abstract_map/flask/debughelpers.py
delete mode 100644 laplas/tools/abstract_map/flask/globals.py
delete mode 100644 laplas/tools/abstract_map/flask/helpers.py
delete mode 100644 laplas/tools/abstract_map/flask/json/__init__.py
delete mode 100644 laplas/tools/abstract_map/flask/json/tag.py
delete mode 100644 laplas/tools/abstract_map/flask/logging.py
delete mode 100644 laplas/tools/abstract_map/flask/scaffold.py
delete mode 100644 laplas/tools/abstract_map/flask/sessions.py
delete mode 100644 laplas/tools/abstract_map/flask/signals.py
delete mode 100644 laplas/tools/abstract_map/flask/templating.py
delete mode 100644 laplas/tools/abstract_map/flask/testing.py
delete mode 100644 laplas/tools/abstract_map/flask/typing.py
delete mode 100644 laplas/tools/abstract_map/flask/views.py
delete mode 100644 laplas/tools/abstract_map/flask/wrappers.py
delete mode 100644 laplas/tools/abstract_map/main.py
diff --git a/laplas/abstract_map/objects/camera.py b/laplas/abstract_map/objects/camera.py
new file mode 100644
index 00000000..6922b968
--- /dev/null
+++ b/laplas/abstract_map/objects/camera.py
@@ -0,0 +1,46 @@
+import pygame
+
+class camera:
+ def __init__(self, screen, surface, new_x, new_y) -> None:
+ self.width = 800
+ self.height = 600
+ self.dragging = False
+ self.last_mouse_x = 0
+ self.last_mouse_y = 0
+
+ self.x = new_x
+ self.y = new_y
+
+ self.screen = screen
+ self.render_surface = surface
+ self.camera_rect = pygame.Rect(self.x, self.y, self.width, self.height)
+
+ print(f"Camera initialized at ({self.x}, {self.y})") # Отладочный вывод
+
+ def process(self):
+ for event in pygame.event.get():
+ if event.type == pygame.MOUSEBUTTONDOWN:
+ if event.button == 1:
+ self.dragging = True
+ self.last_mouse_x, self.last_mouse_y = event.pos
+ print(f"Started dragging at ({self.last_mouse_x}, {self.last_mouse_y})")
+
+ if event.type == pygame.MOUSEBUTTONUP:
+ if event.button == 1:
+ self.dragging = False
+ print("Stopped dragging")
+
+ if event.type == pygame.MOUSEMOTION:
+ if self.dragging:
+ mouse_x, mouse_y = event.pos
+ dx = mouse_x - self.last_mouse_x
+ dy = mouse_y - self.last_mouse_y
+ self.x -= dx
+ self.y -= dy
+ self.last_mouse_x, self.last_mouse_y = mouse_x, mouse_y
+
+ print(f"Dragging to ({self.x}, {self.y})")
+
+ self.camera_rect.topleft = (self.x, self.y)
+ self.screen.blit(self.render_surface, (0, 0), self.camera_rect)
+ print(f"Camera rect top-left at ({self.camera_rect.x}, {self.camera_rect.y})") # Отладочный вывод
diff --git a/laplas/tools/abstract_map/objects/object.py b/laplas/abstract_map/objects/object.py
similarity index 100%
rename from laplas/tools/abstract_map/objects/object.py
rename to laplas/abstract_map/objects/object.py
diff --git a/laplas/tools/abstract_map/overmap.py b/laplas/abstract_map/overmap.py
similarity index 74%
rename from laplas/tools/abstract_map/overmap.py
rename to laplas/abstract_map/overmap.py
index 0eede1eb..52384f11 100644
--- a/laplas/tools/abstract_map/overmap.py
+++ b/laplas/abstract_map/overmap.py
@@ -1,13 +1,11 @@
import pygame
from objects import object
-import camera
+from objects import camera
camera_width, camera_height = 800, 600
class overmap:
- pygame.init()
-
def __init__(self, size_x: int, size_y: int, visual: bool) -> None:
# A map full size
self.size_x = size_x
@@ -23,19 +21,22 @@ def __init__(self, size_x: int, size_y: int, visual: bool) -> None:
self.screen = pygame.display.set_mode((camera_width, camera_height))
self.create_map()
self.create_camera()
+
+ self.clock = pygame.time.Clock()
pygame.display.set_caption("Large Map with Coordinate System")
+ self.camera = camera
## Overmap functions
# Actually creates a non physical map, ans setups a cordinates system
def create_map(self):
self.map_holder = pygame.Surface((self.size_x, self.size_y))
- self.level_surface.fill((25, 25, 25))
+ self.map_holder.fill((25, 25, 25))
def create_camera(self):
- camera_x, camera_y = 0, 0
- self.camera = camera(self.map_holder, self.screen, camera_x, camera_y)
+ camera_x, camera_y = 1, 1
+# self.camera = camera(self.map_holder, self.screen, camera_x, camera_y)
## Function for manipulate with objects
def create_object(self, obj, args):
@@ -48,9 +49,15 @@ def adjust_speed(self):
pass
def process(self):
- self.camera.process()
+# self.camera.process(self.camera)
+ self.clock.tick(60)
if(not self.all_object.__len__()):
return
for obj in self.all_object:
obj.process()
+
+
+def process_map(state: bool):
+ while(state):
+ overmap.process()
diff --git a/laplas/abstract_map/pygame-2.6.0.dist-info/INSTALLER b/laplas/abstract_map/pygame-2.6.0.dist-info/INSTALLER
new file mode 100644
index 00000000..a1b589e3
--- /dev/null
+++ b/laplas/abstract_map/pygame-2.6.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+pip
diff --git a/laplas/abstract_map/pygame-2.6.0.dist-info/METADATA b/laplas/abstract_map/pygame-2.6.0.dist-info/METADATA
new file mode 100644
index 00000000..8e53e0e8
--- /dev/null
+++ b/laplas/abstract_map/pygame-2.6.0.dist-info/METADATA
@@ -0,0 +1,314 @@
+Metadata-Version: 2.1
+Name: pygame
+Version: 2.6.0
+Summary: Python Game Development
+Home-page: https://www.pygame.org
+Author: A community project.
+Author-email: pygame@pygame.org
+License: LGPL
+Project-URL: Documentation, https://pygame.org/docs
+Project-URL: Bug Tracker, https://github.com/pygame/pygame/issues
+Project-URL: Source, https://github.com/pygame/pygame
+Project-URL: Twitter, https://twitter.com/pygame_org
+Platform: UNKNOWN
+Classifier: Development Status :: 6 - Mature
+Classifier: License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)
+Classifier: Programming Language :: Assembly
+Classifier: Programming Language :: C
+Classifier: Programming Language :: Cython
+Classifier: Programming Language :: Objective C
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Programming Language :: Python :: 3.8
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Games/Entertainment
+Classifier: Topic :: Multimedia :: Sound/Audio
+Classifier: Topic :: Multimedia :: Sound/Audio :: MIDI
+Classifier: Topic :: Multimedia :: Sound/Audio :: Players
+Classifier: Topic :: Multimedia :: Graphics
+Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera
+Classifier: Topic :: Multimedia :: Graphics :: Capture :: Screen Capture
+Classifier: Topic :: Multimedia :: Graphics :: Graphics Conversion
+Classifier: Topic :: Multimedia :: Graphics :: Viewers
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Unix
+Classifier: Operating System :: MacOS
+Requires-Python: >=3.6
+Description-Content-Type: text/x-rst
+
+.. image:: https://raw.githubusercontent.com/pygame/pygame/main/docs/reST/_static/pygame_logo.svg
+ :alt: pygame
+ :target: https://www.pygame.org/
+
+
+|AppVeyorBuild| |PyPiVersion| |PyPiLicense|
+|Python3| |GithubCommits| |BlackFormatBadge|
+
+Pygame_ is a free and open-source cross-platform library
+for the development of multimedia applications like video games using Python.
+It uses the `Simple DirectMedia Layer library`_ and several other
+popular libraries to abstract the most common functions, making writing
+these programs a more intuitive task.
+
+`We need your help`_ to make pygame the best it can be!
+New contributors are welcome.
+
+
+Installation
+------------
+
+Before installing pygame, you must check that Python is installed
+on your machine. To find out, open a command prompt (if you have
+Windows) or a terminal (if you have MacOS or Linux) and type this:
+::
+
+ python --version
+
+
+If a message such as "Python 3.8.10" appears, it means that Python
+is correctly installed. If an error message appears, it means that
+it is not installed yet. You must then go to the `official website
+`_ to download it.
+
+Once Python is installed, you have to perform a final check: you have
+to see if pip is installed. Generally, pip is pre-installed with
+Python but we are never sure. Same as for Python, type the following
+command:
+::
+
+ pip --version
+
+
+If a message such as "pip 20.0.2 from /usr/lib/python3/dist-packages/pip
+(python 3.8)" appears, you are ready to install pygame! To install
+it, enter this command:
+::
+
+ pip install pygame
+
+Once pygame is installed, quickly test your library by entering the following
+command, which opens one of the many example games that comes pre-installed:
+::
+
+ python3 -m pygame.examples.aliens
+
+
+If this doesn’t work, the `Getting Started
+`_ section of the official
+website has more information for platform specific issues, such as adding
+python to your machine’s PATH settings
+
+
+Help
+----
+
+If you are just getting started with pygame, you should be able to
+get started fairly quickly. Pygame comes with many tutorials and
+introductions. There is also full reference documentation for the
+entire library. Browse the documentation on the `docs page`_. You
+can also browse the documentation locally by running
+``python -m pygame.docs`` in your terminal. If the docs aren't found
+locally, it'll launch the online website instead.
+
+The online documentation stays up to date with the development version
+of pygame on GitHub. This may be a bit newer than the version of pygame
+you are using. To upgrade to the latest full release, run
+``pip install pygame --upgrade`` in your terminal.
+
+Best of all, the examples directory has many playable small programs
+which can get you started playing with the code right away.
+
+
+Features
+----------
+
+Pygame is a powerful library for game development, offering a wide
+range of features to simplify your coding journey. Let's delve into
+what pygame has to offer:
+
+Graphics - With pygame, creating dynamic and engaging graphics has
+never been easier. The library provides simple yet effective tools for
+2D graphics and animation, including support for images, rectangles,
+and polygon shapes. Whether you're a seasoned game developer or just
+starting out, pygame has you covered.
+
+Sound - Pygame also includes support for playing and manipulating sound
+and music, making it easy to add sound effects and background music to
+your games. With support for WAV, MP3, and OGG file formats, you have
+plenty of options to choose from.
+
+Input - Pygame provides intuitive functions for handling keyboard, mouse,
+and joystick input, allowing you to quickly and easily implement player
+controls in your games. No more struggling with complex input code, pygame
+makes it simple.
+
+Game Development - Lastly, pygame provides a comprehensive suite of tools
+and features specifically designed for game development. From collision
+detection to sprite management, pygame has everything you need to create
+exciting and engaging games. Whether you're building a platformer, puzzle
+game, or anything in between, pygame has you covered.
+
+
+Building From Source
+--------------------
+
+If you want to use features that are currently in development,
+or you want to contribute to pygame, you will need to build pygame
+locally from its source code, rather than pip installing it.
+
+Installing from source is fairly automated. The most work will
+involve compiling and installing all the pygame dependencies. Once
+that is done, run the ``setup.py`` script which will attempt to
+auto-configure, build, and install pygame.
+
+Much more information about installing and compiling is available
+on the `Compilation wiki page`_.
+
+Contribute
+----------
+
+* `Documentation Contributions `_ - Guidelines for contributing to the main documentations
+* `Writing your first unit test `_ - Step by step guide on how to write your first unit test in Python for Pygame.
+* `How to Hack Pygame `_ - Information on hacking, developing, and modifying Pygame
+* `Issue Tracker for beginners `_ - A way for beginners to contribute to the project
+* `Bugs & Patches `_ - Report bugs
+* `Communication tools `_ - More information and ways to get in touch with the Pygame team
+
+
+Credits
+-------
+
+Thanks to everyone who has helped contribute to this library.
+Special thanks are also in order.
+
+* Marcus Von Appen: many changes, and fixes, 1.7.1+ freebsd maintainer
+* Lenard Lindstrom: the 1.8+ windows maintainer, many changes, and fixes
+* Brian Fisher for svn auto builder, bug tracker and many contributions
+* Rene Dudfield: many changes, and fixes, 1.7+ release manager/maintainer
+* Phil Hassey for his work on the pygame.org website
+* DR0ID for his work on the sprite module
+* Richard Goedeken for his smoothscale function
+* Ulf Ekström for his pixel perfect collision detection code
+* Pete Shinners: original author
+* David Clark for filling the right-hand-man position
+* Ed Boraas and Francis Irving: Debian packages
+* Maxim Sobolev: FreeBSD packaging
+* Bob Ippolito: MacOS and OS X porting (much work!)
+* Jan Ekhol, Ray Kelm, and Peter Nicolai: putting up with early design ideas
+* Nat Pryce for starting our unit tests
+* Dan Richter for documentation work
+* TheCorruptor for his incredible logos and graphics
+* Nicholas Dudfield: many test improvements
+* Alex Folkner for pygame-ctypes
+
+Thanks to those sending in patches and fixes: Niki Spahiev, Gordon
+Tyler, Nathaniel Pryce, Dave Wallace, John Popplewell, Michael Urman,
+Andrew Straw, Michael Hudson, Ole Martin Bjoerndalen, Herve Cauwelier,
+James Mazer, Lalo Martins, Timothy Stranex, Chad Lester, Matthias
+Spiller, Bo Jangeborg, Dmitry Borisov, Campbell Barton, Diego Essaya,
+Eyal Lotem, Regis Desgroppes, Emmanuel Hainry, Randy Kaelber
+Matthew L Daniel, Nirav Patel, Forrest Voight, Charlie Nolan,
+Frankie Robertson, John Krukoff, Lorenz Quack, Nick Irvine,
+Michael George, Saul Spatz, Thomas Ibbotson, Tom Rothamel, Evan Kroske,
+Cambell Barton.
+
+And our bug hunters above and beyond: Angus, Guillaume Proux, Frank
+Raiser, Austin Henry, Kaweh Kazemi, Arturo Aldama, Mike Mulcheck,
+Michael Benfield, David Lau
+
+There's many more folks out there who've submitted helpful ideas, kept
+this project going, and basically made our life easier. Thanks!
+
+Many thank you's for people making documentation comments, and adding to the
+pygame.org wiki.
+
+Also many thanks for people creating games and putting them on the
+pygame.org website for others to learn from and enjoy.
+
+Lots of thanks to James Paige for hosting the pygame bugzilla.
+
+Also a big thanks to Roger Dingledine and the crew at SEUL.ORG for our
+excellent hosting.
+
+Dependencies
+------------
+
+Pygame is obviously strongly dependent on SDL and Python. It also
+links to and embeds several other smaller libraries. The font
+module relies on SDL_ttf, which is dependent on freetype. The mixer
+(and mixer.music) modules depend on SDL_mixer. The image module
+depends on SDL_image, which also can use libjpeg and libpng. The
+transform module has an embedded version of SDL_rotozoom for its
+own rotozoom function. The surfarray module requires the Python
+NumPy package for its multidimensional numeric arrays.
+Dependency versions:
+
+
++----------+------------------------+
+| CPython | >= 3.6 (Or use PyPy3) |
++----------+------------------------+
+| SDL | >= 2.0.8 |
++----------+------------------------+
+| SDL_mixer| >= 2.0.0 |
++----------+------------------------+
+| SDL_image| >= 2.0.2 |
++----------+------------------------+
+| SDL_ttf | >= 2.0.11 |
++----------+------------------------+
+| SDL_gfx | (Optional, vendored in)|
++----------+------------------------+
+| NumPy | >= 1.6.2 (Optional) |
++----------+------------------------+
+
+
+
+License
+-------
+
+This library is distributed under `GNU LGPL version 2.1`_, which can
+be found in the file ``docs/LGPL.txt``. We reserve the right to place
+future versions of this library under a different license.
+
+This basically means you can use pygame in any project you want,
+but if you make any changes or additions to pygame itself, those
+must be released with a compatible license (preferably submitted
+back to the pygame project). Closed source and commercial games are fine.
+
+The programs in the ``examples`` subdirectory are in the public domain.
+
+See docs/licenses for licenses of dependencies.
+
+
+.. |AppVeyorBuild| image:: https://ci.appveyor.com/api/projects/status/x4074ybuobsh4myx?svg=true
+ :target: https://ci.appveyor.com/project/pygame/pygame
+
+.. |PyPiVersion| image:: https://img.shields.io/pypi/v/pygame.svg?v=1
+ :target: https://pypi.python.org/pypi/pygame
+
+.. |PyPiLicense| image:: https://img.shields.io/pypi/l/pygame.svg?v=1
+ :target: https://pypi.python.org/pypi/pygame
+
+.. |Python3| image:: https://img.shields.io/badge/python-3-blue.svg?v=1
+
+.. |GithubCommits| image:: https://img.shields.io/github/commits-since/pygame/pygame/2.1.2.svg
+ :target: https://github.com/pygame/pygame/compare/2.1.2...main
+
+.. |BlackFormatBadge| image:: https://img.shields.io/badge/code%20style-black-000000.svg
+ :target: https://github.com/psf/black
+
+.. _pygame: https://www.pygame.org
+.. _Simple DirectMedia Layer library: https://www.libsdl.org
+.. _We need your help: https://www.pygame.org/contribute.html
+.. _Compilation wiki page: https://www.pygame.org/wiki/Compilation
+.. _docs page: https://www.pygame.org/docs/
+.. _GNU LGPL version 2.1: https://www.gnu.org/copyleft/lesser.html
+
+
diff --git a/laplas/abstract_map/pygame-2.6.0.dist-info/RECORD b/laplas/abstract_map/pygame-2.6.0.dist-info/RECORD
new file mode 100644
index 00000000..d75b439e
--- /dev/null
+++ b/laplas/abstract_map/pygame-2.6.0.dist-info/RECORD
@@ -0,0 +1,771 @@
+../../include/python/pygame/_blit_info.h,sha256=wRHRXxQ9k7NBMHHPymtStYuI8Prwqbdu0jE0gouqUYc,470
+../../include/python/pygame/_camera.h,sha256=T0VYAfQxm0c4zww_BZaJGz4exa4z0FdEf3RSN_W2E-E,839
+../../include/python/pygame/_pygame.h,sha256=UhRJF2E7W8CVfKAW5cySBOxf8tvzyAX0tDjEhUrJPeM,11726
+../../include/python/pygame/_surface.h,sha256=Bbi9rW0SqwGs6THID0l6eB5d-5h-kxW7517TdqoxEZM,957
+../../include/python/pygame/camera.h,sha256=SDNTLUp5qj5HHZyrfNR8f-t-lpSNHxj3EXO-WqQNPYo,5605
+../../include/python/pygame/font.h,sha256=VHcKhYtIHduegTXEf1hbmbxCwN5IrqsJch2HaxEtB6I,348
+../../include/python/pygame/freetype.h,sha256=LbGY6saj9oakyoeGSlWlirSHySOpeoTKKOF-DO4zLRs,3245
+../../include/python/pygame/include/_pygame.h,sha256=8ftev1IbOAg9AFaFQBzeA4bnYLM78lriFjjtqzyGIFQ,30436
+../../include/python/pygame/include/bitmask.h,sha256=tGzYwZ407sMIHDQG7xeXBQCRmhZZ4wp-yTerhUmIlCU,4952
+../../include/python/pygame/include/pgcompat.h,sha256=l-At6iLGU8EeMqYdsDMATS2zfkviXWe1Rs7lnOwO7oc,1944
+../../include/python/pygame/include/pgimport.h,sha256=3VrUyOZC6kbEdOcXn1QbOZ3nusBAuFSQxYgDadHNTPI,2636
+../../include/python/pygame/include/pgplatform.h,sha256=4y5BDK1oKeK2i5pO3UfZAbZXv_KdNdco5ZBbrtBsrtc,2312
+../../include/python/pygame/include/pygame.h,sha256=OsEc_zNPFlXo4owOhGRqH4cbuYMtNJ_7K9d_Te--OEU,1245
+../../include/python/pygame/include/pygame_bufferproxy.h,sha256=Poh7HsIjugo3NFeKjItZe40_BavF5V7re8bVFjkpmfU,1834
+../../include/python/pygame/include/pygame_font.h,sha256=JKPbDFQdh_BAuz5F9S37iBSZbfjnL2scUlkS1geLd4s,1501
+../../include/python/pygame/include/pygame_freetype.h,sha256=VNyvy7xukNOXymg3IMZne9-iVu3sI5LvKLagfkWpKAk,1346
+../../include/python/pygame/include/pygame_mask.h,sha256=ONXIz3M3MPF4BlPSS2xRquysEYjZZf7AP2JRro8j4I0,1303
+../../include/python/pygame/include/pygame_mixer.h,sha256=HthA7STa9TLomwQQswroyAmAd72pywDu_UCLfIV71is,2021
+../../include/python/pygame/include/sse2neon.h,sha256=DcazZmLfny6MVJFIUlWbsdI39ZWQWGwmCJDuFEVZsw0,237885
+../../include/python/pygame/mask.h,sha256=Y7OqzNUqQQHchUsSlvd-ja5d9IgAfSV2uFlaa8_5Lys,153
+../../include/python/pygame/mixer.h,sha256=HJMd0Ho0DrdGBdPMDo_egSqkVxZnUZPMEQyPJMIw6_M,348
+../../include/python/pygame/palette.h,sha256=dzARYIsQdHAaV8ypCrQbYRWFisXYXABD4ToMlzYKojg,7057
+../../include/python/pygame/pgarrinter.h,sha256=alsw7p6X7ukOB1o3curyrjWOcGHgVCQgCvS1D9FtiRc,1060
+../../include/python/pygame/pgbufferproxy.h,sha256=tqMDkdkH40QoYJ3NtTjiknAnSMh0i1sfNMaow3npvKI,179
+../../include/python/pygame/pgcompat.h,sha256=Ro6kJ6ak2LQSHR4LmwnulolGuQO0KW6UZiu02iP-1_I,737
+../../include/python/pygame/pgopengl.h,sha256=bbIysbLph5paPfeE2nnrQBIrq8iZz4T4pGfz2mYPuRw,606
+../../include/python/pygame/pgplatform.h,sha256=LuZxNMbDYRCgjDk1_ruNTOiQsyO_FcI9qxNtJeAUpXQ,553
+../../include/python/pygame/pygame.h,sha256=AQcZWIoAWGmN9fYBynCXxc81hd9lcwxBwWeq8WZjTKE,1083
+../../include/python/pygame/scrap.h,sha256=d36ZWp5LM7o9RRcHCFiHwAT0aJZN_c17wTNu_LYHuEE,4704
+../../include/python/pygame/simd_blitters.h,sha256=8Bv4j0uHf4ErZMeJeXjnrnVvKBQdSLmKcrEVgbYJlPc,2494
+../../include/python/pygame/surface.h,sha256=6Goq2WvnwB_mYosiq4wZMIzPXn7uoCyA_fTK4uKtf6I,14584
+pygame-2.6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
+pygame-2.6.0.dist-info/METADATA,sha256=0E_Q0bagnKiFLtYrVz5ed36rymbEf4fS8oxye5air6s,12898
+pygame-2.6.0.dist-info/RECORD,,
+pygame-2.6.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pygame-2.6.0.dist-info/WHEEL,sha256=zukbZftcRdNfJ55Q_RYV9JZ3uVyqWKyq-wDJbmszs_U,101
+pygame-2.6.0.dist-info/entry_points.txt,sha256=ecT3iHcE_RL35uzQ2sXUdmOZbqq1XtPQNmbotP1FOQY,64
+pygame-2.6.0.dist-info/top_level.txt,sha256=ABXdFGIAE2g9m2VOzQPaLa917r6XEu6d96RqIzvAWCs,7
+pygame/SDL2.dll,sha256=Ug0EWbke-jL7zPkCepyh_FquZX5nnOjpDxefnPWv0nk,2499072
+pygame/SDL2_image.dll,sha256=HjZK91_uDINQb739TVsOOGxOnGoz3b3axh3bEx42AZQ,125440
+pygame/SDL2_mixer.dll,sha256=Kg_F6fcsLq7DJAy4K3WUpYzNpglIWYHyVrlNCk3Y1vg,291840
+pygame/SDL2_ttf.dll,sha256=_N-r386GjrM_dRQCX_WcG7bEGPG81qziMAqc1AU-HWM,1552384
+pygame/__init__.py,sha256=cfk8mST6YzXpJH83W86aq5Sj6HzZYCZ8RYJUUR3KRt4,9483
+pygame/__init__.pyi,sha256=javlxYV7i0wDGZ69E_J2x1PLmXoEN-pfcF0MCcLqKbY,20400
+pygame/__pycache__/__init__.cpython-36.pyc,,
+pygame/__pycache__/_camera_opencv.cpython-36.pyc,,
+pygame/__pycache__/_camera_vidcapture.cpython-36.pyc,,
+pygame/__pycache__/camera.cpython-36.pyc,,
+pygame/__pycache__/colordict.cpython-36.pyc,,
+pygame/__pycache__/cursors.cpython-36.pyc,,
+pygame/__pycache__/draw_py.cpython-36.pyc,,
+pygame/__pycache__/fastevent.cpython-36.pyc,,
+pygame/__pycache__/freetype.cpython-36.pyc,,
+pygame/__pycache__/ftfont.cpython-36.pyc,,
+pygame/__pycache__/locals.cpython-36.pyc,,
+pygame/__pycache__/macosx.cpython-36.pyc,,
+pygame/__pycache__/midi.cpython-36.pyc,,
+pygame/__pycache__/pkgdata.cpython-36.pyc,,
+pygame/__pycache__/sndarray.cpython-36.pyc,,
+pygame/__pycache__/sprite.cpython-36.pyc,,
+pygame/__pycache__/surfarray.cpython-36.pyc,,
+pygame/__pycache__/sysfont.cpython-36.pyc,,
+pygame/__pycache__/version.cpython-36.pyc,,
+pygame/__pyinstaller/__init__.py,sha256=-c4Zo8nQGKAm8wc_LDscxMtK7zr_YhZwRnC9CMruUBE,72
+pygame/__pyinstaller/__pycache__/__init__.cpython-36.pyc,,
+pygame/__pyinstaller/__pycache__/hook-pygame.cpython-36.pyc,,
+pygame/__pyinstaller/hook-pygame.py,sha256=F54YZHmQpTIBH265sM0r5_8_YZYWo0IBhG_QVAQybgI,1368
+pygame/_camera.cp36-win_amd64.pyd,sha256=ycgz6kjDlOUoYMYgPigW6ezRwv1U2ritpG9pPSBV_6w,17408
+pygame/_camera_opencv.py,sha256=DiPrxXiIGUxoz4lSONGs04sOo9pEA8RKQ9cnNO7JC2M,5454
+pygame/_camera_vidcapture.py,sha256=mbPFnTU0520qXpj0qt6ei3YQ1_1OFRuDU-5fXFm-yos,3403
+pygame/_common.pyi,sha256=YPuDlQLtP8KzA-qA9LnYkOhS9zzSvA5ENwWHI2WSvoI,1349
+pygame/_freetype.cp36-win_amd64.pyd,sha256=Zh6qKUHscfFSapl6RxJ3Urgc0gvw-Oesths86ZjEFoM,78336
+pygame/_sdl2/__init__.py,sha256=gmSh3cXyxHqEXwbck_mNOmoW--itmGMwonEHQnY49Zo,248
+pygame/_sdl2/__init__.pyi,sha256=fWR2P9epfYw8F_xyyMBoUxDZwJTIroizEnkN_eLf2rw,98
+pygame/_sdl2/__pycache__/__init__.cpython-36.pyc,,
+pygame/_sdl2/audio.cp36-win_amd64.pyd,sha256=ij7EWpWSC0yOejk09M92KAevVSX9k4HXgD_0xOW4Tls,165888
+pygame/_sdl2/audio.pyi,sha256=DRRipL7rySE-0TddObb1A-l99rWQPK4YI36hvyHj4dw,1300
+pygame/_sdl2/controller.cp36-win_amd64.pyd,sha256=lSaE7lHNpJbw0MRcK7v4b_EK1JB6_0YRy_piWwuW6xE,104960
+pygame/_sdl2/controller.pyi,sha256=8WZ0Qf5iHIJTElgu29LeAhZO3Htbo00b8oL3aohK5P4,1165
+pygame/_sdl2/mixer.cp36-win_amd64.pyd,sha256=uCZi3jBZ4NiLVy-97IjRDAM1yVDSqoHGrZyvTSln0AQ,144896
+pygame/_sdl2/sdl2.cp36-win_amd64.pyd,sha256=cl_JktCoC3sHwWkXtQniyCnP_MUyF1WpuLRBW_rmvgA,42496
+pygame/_sdl2/sdl2.pyi,sha256=n0Qa04-g3FdR7wiMEC_4ZhmAojvQTXGjTl1z13OWU70,338
+pygame/_sdl2/touch.cp36-win_amd64.pyd,sha256=BuTpW-Sza4SPW6Ja1lteNCZ9UbsOiHzY0DKKvgRCYCg,13824
+pygame/_sdl2/touch.pyi,sha256=-vYGJsSC18E2gitH10HIv_V_DUc6DIeCb55zuQp3jPc,231
+pygame/_sdl2/video.cp36-win_amd64.pyd,sha256=ifatdKVx-KSTwesyo-L8oz1CLAdW356TSi48K1M4-vM,228864
+pygame/_sdl2/video.pyi,sha256=1ziRZwVFLhFTYubfNBwtyIF7hBWlCiscXul7V4dMcS8,4677
+pygame/_sprite.cp36-win_amd64.pyd,sha256=25QlNSngwNaYbctMdM3gBxTLqIB3F4r1sTTSnGjPg-c,318464
+pygame/base.cp36-win_amd64.pyd,sha256=4ffRlz76iLc5r6hxsODjEU42D1XPbpzTNr0En6I-qsw,30720
+pygame/base.pyi,sha256=MPRqq61kkfR-s7IXcoZyahrRP3VzERTED46GCuVacpg,586
+pygame/bufferproxy.cp36-win_amd64.pyd,sha256=DqG-V4LwXemOq4yDX_W14mDJ1Je0XDpdEOzf8HjfHcU,18432
+pygame/bufferproxy.pyi,sha256=znPTlMWUbbJI2iCUz8F3pY0guxHOzVnQhpnUaPhfB2E,458
+pygame/camera.py,sha256=MklsW1Bj7vEm8_33RMWZhelsgDQF7yy5ZrvGyD4CIG8,6083
+pygame/camera.pyi,sha256=yeFKyldBEFDOUwMRXsqPcDdH2hOgeoth9fOuBJ8Dc8Y,1624
+pygame/color.cp36-win_amd64.pyd,sha256=P4t_QGkkvYS1ZPnrWc_jat5UGb_T_ihOORohosvyAjk,35840
+pygame/color.pyi,sha256=rBSvRlXAFlP4kzuVdI_z5n9TkCcZpz6_2XEAvYHG0gk,2050
+pygame/colordict.py,sha256=xS-mwTxatoVa3Howpp53otRNZ3p-b1MzZChlvy0cuic,25773
+pygame/constants.cp36-win_amd64.pyd,sha256=s5QdGgyOAjCxdd_OB08KHlzrJ7mPinQfJWcfWUEOeag,50688
+pygame/constants.pyi,sha256=PqbIVEVcVE9pKTaCFd9RExuYFf4i-1H_u_zjx6KP44U,9902
+pygame/cursors.py,sha256=kwDhVH_LeFzfvakDmxPYnf8FB3ElmAH8aClRYedi4os,18093
+pygame/cursors.pyi,sha256=rvpf35D0IPYU2SBH30K46RRmigQfQH4SyQfb7TwnNiQ,2090
+pygame/display.cp36-win_amd64.pyd,sha256=EMtGCTQF_SQhnlkNeiopIck1eg3f7QFOFIUXG3Dctok,45056
+pygame/display.pyi,sha256=5rC8JySsJTs0GWDaAG29lQan4HhtzIA0ilVvsi3Vvqs,2273
+pygame/docs/__main__.py,sha256=1dEMTojlXYV8qZ_T9h2f_AYdpPwwyCqmHBC8iuJ2KGI,995
+pygame/docs/__pycache__/__main__.cpython-36.pyc,,
+pygame/docs/generated/LGPL.txt,sha256=oZDcnIBDdV2Q-LCnX6ZrnkLUr0yYC_XdxjPwEk2zzuc,26430
+pygame/docs/generated/_images/AdvancedInputOutput1.gif,sha256=uSCxW5dFtO7PYQyoJglWJTe_aRYFfOav5DjnPkKBzKg,5649
+pygame/docs/generated/_images/AdvancedInputOutput11.gif,sha256=uSCxW5dFtO7PYQyoJglWJTe_aRYFfOav5DjnPkKBzKg,5649
+pygame/docs/generated/_images/AdvancedInputOutput2.gif,sha256=2UMDweIgRefzHFTJcJgbDsWgy-SlyOYaw2AKD6NKwmM,72233
+pygame/docs/generated/_images/AdvancedInputOutput21.gif,sha256=2UMDweIgRefzHFTJcJgbDsWgy-SlyOYaw2AKD6NKwmM,72233
+pygame/docs/generated/_images/AdvancedInputOutput3.gif,sha256=ez4Y7Yy0LsNH6UPYybfGckLmzutcLh7VIisajgFa4O8,6294
+pygame/docs/generated/_images/AdvancedInputOutput31.gif,sha256=ez4Y7Yy0LsNH6UPYybfGckLmzutcLh7VIisajgFa4O8,6294
+pygame/docs/generated/_images/AdvancedInputOutput4.gif,sha256=2-rK9cGngrgpJLW404oI3lG4NQnoT-UNwzA3Up48YIc,29185
+pygame/docs/generated/_images/AdvancedInputOutput41.gif,sha256=2-rK9cGngrgpJLW404oI3lG4NQnoT-UNwzA3Up48YIc,29185
+pygame/docs/generated/_images/AdvancedInputOutput5.gif,sha256=C6N4d_JD4QNMjLwPdINCOFWufyD4vM7b5LpR2GaHMcY,37349
+pygame/docs/generated/_images/AdvancedInputOutput51.gif,sha256=C6N4d_JD4QNMjLwPdINCOFWufyD4vM7b5LpR2GaHMcY,37349
+pygame/docs/generated/_images/AdvancedOutputAlpha1.gif,sha256=MH3CmK658WpN_u56GL9CV4YMFky9GLaKh0CAcixkMrA,14915
+pygame/docs/generated/_images/AdvancedOutputAlpha11.gif,sha256=MH3CmK658WpN_u56GL9CV4YMFky9GLaKh0CAcixkMrA,14915
+pygame/docs/generated/_images/AdvancedOutputAlpha2.gif,sha256=9E2YAIULWHZsAELfqLf5gdqL7-uE-Ebg4uqKgbSyVYk,71819
+pygame/docs/generated/_images/AdvancedOutputAlpha21.gif,sha256=9E2YAIULWHZsAELfqLf5gdqL7-uE-Ebg4uqKgbSyVYk,71819
+pygame/docs/generated/_images/AdvancedOutputAlpha3.gif,sha256=7gHdbhSKaUpyXgOC7GSusTdN7oJQvjGFY_o8DQNZ2Fc,30380
+pygame/docs/generated/_images/AdvancedOutputAlpha31.gif,sha256=7gHdbhSKaUpyXgOC7GSusTdN7oJQvjGFY_o8DQNZ2Fc,30380
+pygame/docs/generated/_images/AdvancedOutputProcess1.gif,sha256=QihTI3ThxtEnPjQ0qZaYF4rKj5GxSwDfFgfUmFjvcOw,15951
+pygame/docs/generated/_images/AdvancedOutputProcess11.gif,sha256=QihTI3ThxtEnPjQ0qZaYF4rKj5GxSwDfFgfUmFjvcOw,15951
+pygame/docs/generated/_images/AdvancedOutputProcess2.gif,sha256=qP09Je0xWwST1CoAs9BCy7_vq6OB69opna8kejTln50,1868
+pygame/docs/generated/_images/AdvancedOutputProcess21.gif,sha256=qP09Je0xWwST1CoAs9BCy7_vq6OB69opna8kejTln50,1868
+pygame/docs/generated/_images/AdvancedOutputProcess3.gif,sha256=4WhoBbEheizUdZfs_pFEBGGAm9aoetw3tJhgcNgVIyY,1912
+pygame/docs/generated/_images/AdvancedOutputProcess31.gif,sha256=4WhoBbEheizUdZfs_pFEBGGAm9aoetw3tJhgcNgVIyY,1912
+pygame/docs/generated/_images/AdvancedOutputProcess4.gif,sha256=m-gUJNOn6AuXT7FpKF6HRR8A6ytWorY9Y07N2uZaSIQ,14500
+pygame/docs/generated/_images/AdvancedOutputProcess41.gif,sha256=m-gUJNOn6AuXT7FpKF6HRR8A6ytWorY9Y07N2uZaSIQ,14500
+pygame/docs/generated/_images/AdvancedOutputProcess5.gif,sha256=GCi9KGUIhQTFg-HLEnp2GEgrnQl8_2KftS3N_UkuEH8,16896
+pygame/docs/generated/_images/AdvancedOutputProcess51.gif,sha256=GCi9KGUIhQTFg-HLEnp2GEgrnQl8_2KftS3N_UkuEH8,16896
+pygame/docs/generated/_images/AdvancedOutputProcess6.gif,sha256=nzV_M0JoA4aDyGwpe8lWoUFd5c1PK-WxUI-lF8WzfQQ,34058
+pygame/docs/generated/_images/AdvancedOutputProcess61.gif,sha256=nzV_M0JoA4aDyGwpe8lWoUFd5c1PK-WxUI-lF8WzfQQ,34058
+pygame/docs/generated/_images/Bagic-INPUT-resultscreen.png,sha256=RDZbxtVyFMJXdZA8wouSyJJjXf2MQ2WYTBzVotsDH88,5973
+pygame/docs/generated/_images/Bagic-INPUT-resultscreen1.png,sha256=RDZbxtVyFMJXdZA8wouSyJJjXf2MQ2WYTBzVotsDH88,5973
+pygame/docs/generated/_images/Bagic-INPUT-sourcecode.png,sha256=3F2c3AnravGgsRMxaNXlaekyqrhMl2cwYySJxhj7L0A,77061
+pygame/docs/generated/_images/Bagic-INPUT-sourcecode1.png,sha256=3F2c3AnravGgsRMxaNXlaekyqrhMl2cwYySJxhj7L0A,77061
+pygame/docs/generated/_images/Bagic-PROCESS-resultscreen.png,sha256=hQ1m6S1xhXpcaf0g50VRoOagmsiZZpUeOBx7QgG7Lqs,5348
+pygame/docs/generated/_images/Bagic-PROCESS-resultscreen1.png,sha256=hQ1m6S1xhXpcaf0g50VRoOagmsiZZpUeOBx7QgG7Lqs,5348
+pygame/docs/generated/_images/Bagic-PROCESS-sourcecode.png,sha256=vj0D6wrXFNjIHKmRFZrltZH4nH51zG6YSy94ID2fWos,66070
+pygame/docs/generated/_images/Bagic-PROCESS-sourcecode1.png,sha256=vj0D6wrXFNjIHKmRFZrltZH4nH51zG6YSy94ID2fWos,66070
+pygame/docs/generated/_images/Bagic-ouput-result-screen.png,sha256=Ig1vKczM-l0ebtdjYEHdwcJuqt8IoyUiK-RANEC065k,4819
+pygame/docs/generated/_images/Bagic-ouput-result-screen1.png,sha256=Ig1vKczM-l0ebtdjYEHdwcJuqt8IoyUiK-RANEC065k,4819
+pygame/docs/generated/_images/Basic-ouput-sourcecode.png,sha256=B6OVjvOtA2ZwiEwJyYkK1-tsGyN13y8O9kls1OYvzTo,57466
+pygame/docs/generated/_images/Basic-ouput-sourcecode1.png,sha256=B6OVjvOtA2ZwiEwJyYkK1-tsGyN13y8O9kls1OYvzTo,57466
+pygame/docs/generated/_images/angle_to.png,sha256=vP3M5zZVFf-ooagw-hRTlnhBLbGQvX9gxO7nX_gBgbc,25349
+pygame/docs/generated/_images/camera_average.jpg,sha256=dkXZ7NdHmM69rbcYCpu0vKtqmHF3qh9nPUgZX3wlWDc,20881
+pygame/docs/generated/_images/camera_background.jpg,sha256=exoGN5fT9IKQyMJK_3VrEjfKTvr5yMeoSLCQplD0hes,7493
+pygame/docs/generated/_images/camera_green.jpg,sha256=NpIuT5qRzN5I7TFLva8m_kCAo1cwOuR5R5-Du9kaEo0,10219
+pygame/docs/generated/_images/camera_hsv.jpg,sha256=tfL0KJyxSk5A_KjVZR7MdV-qegBhej5HlXXw2CnoZR8,36673
+pygame/docs/generated/_images/camera_mask.jpg,sha256=0u0yMCldZMvSW1vyO2KK32D-fVYuYpXlNzmWwbdZ__s,18779
+pygame/docs/generated/_images/camera_rgb.jpg,sha256=GN_1jI8mnDJm1bRbjNBmpJETDSAKcVAS9BxmycYMMv0,32488
+pygame/docs/generated/_images/camera_thresh.jpg,sha256=WBYm8M-TxnuKCYEBvmu68iO_r9EYucnENZ5Ew8i6tqk,4346
+pygame/docs/generated/_images/camera_thresholded.jpg,sha256=OMh-3zXV2a-aahnMQlE7ihvxwXy1UADb15c3LzIWgh0,23678
+pygame/docs/generated/_images/camera_yuv.jpg,sha256=Gp0omp1py-_j6Qpv95VIo6EmDO2u5VY83fPWA2Rd6Bk,20105
+pygame/docs/generated/_images/chimpshot.gif,sha256=Yc_ufSFTkZ5NA1IogV2juH5Cr4_ykoI7QcXQvfGYBfc,46010
+pygame/docs/generated/_images/draw_module_example.png,sha256=jAhc1HG8RXjjPnjn9AwncGwfi4dKbaO7rQgqcd5OCeQ,6476
+pygame/docs/generated/_images/intro_ball.gif,sha256=vEs0-OG_j55JZJML48IXhHsN4ZMIWrqlS7dfxd9-hxc,5015
+pygame/docs/generated/_images/intro_blade.jpg,sha256=Aj59Tt9z1mdJeDK89HbWaQ7DVTDKbzoUDT-vNcJnYQo,2631
+pygame/docs/generated/_images/intro_freedom.jpg,sha256=RL-jChKVMdqoS7BN5NGV7hjlxSgU4qaKrj4ZXH2zsI8,7050
+pygame/docs/generated/_images/introduction-Battleship.png,sha256=6iHEhqo_HnXRfmQv9yriYMc3dX8Q2TwGbSGDQREFkN4,165586
+pygame/docs/generated/_images/introduction-Battleship1.png,sha256=6iHEhqo_HnXRfmQv9yriYMc3dX8Q2TwGbSGDQREFkN4,165586
+pygame/docs/generated/_images/introduction-PuyoPuyo.png,sha256=OEMjFSzQc8vJLQryUdkp7lJ3DhIw_yEo5-5nm2vBfrs,31388
+pygame/docs/generated/_images/introduction-PuyoPuyo1.png,sha256=OEMjFSzQc8vJLQryUdkp7lJ3DhIw_yEo5-5nm2vBfrs,31388
+pygame/docs/generated/_images/introduction-TPS.png,sha256=M4ioZMyjR2n7pQIp8UhGRV4m2V_rcXJCuo5lU3V7yGw,136031
+pygame/docs/generated/_images/introduction-TPS1.png,sha256=M4ioZMyjR2n7pQIp8UhGRV4m2V_rcXJCuo5lU3V7yGw,136031
+pygame/docs/generated/_images/joystick_calls.png,sha256=oNAQgfZ8GM5_Z17hoNVrLf1tYeRnths60ZrlhJduWhs,30004
+pygame/docs/generated/_images/pygame_lofi.png,sha256=QBECBUalJHRExXhf7nt-QpsVcu9LQ3RSAYVVS04AX3M,134242
+pygame/docs/generated/_images/pygame_logo.png,sha256=Jc1Lz47pY3mHjEFNzQXV9gx-olUUYCGuFYrutncaTXE,132068
+pygame/docs/generated/_images/pygame_powered.png,sha256=LgbswFcg647alSC5SawDPH96xuTMcWmMqVA6Zvs2K3Q,179911
+pygame/docs/generated/_images/pygame_powered_lowres.png,sha256=9go5WMiAE4fTEmB9w-zatEfROGl1IUWJGBpKoLqy-wU,179911
+pygame/docs/generated/_images/pygame_tiny.png,sha256=BXPk3OkSWdSkqjMkCQ1Dt5WjxZfb4zj4c2ir9U9_Y7Q,15310
+pygame/docs/generated/_images/surfarray_allblack.png,sha256=XEUO2hKFfTfZMyaqbPvM0u3zmETfl___AANBkHn6y-w,125
+pygame/docs/generated/_images/surfarray_flipped.png,sha256=UZ1FpljGrdAnB1UCUTuAfeJJxyqI-_0duSCKqy9UN3w,50835
+pygame/docs/generated/_images/surfarray_redimg.png,sha256=6tlO_tZokQTsfgvD3yNW21nHA5uA9hDWoaL7POrr_qE,23443
+pygame/docs/generated/_images/surfarray_rgbarray.png,sha256=8US5r3GcG_jZBncK-47HG2JLB2ZwQjaSBWhb3ynNs9w,50897
+pygame/docs/generated/_images/surfarray_scaledown.png,sha256=Z68XSoPUvV5bYIhJdkmhZYr42JpQKnC8g9hmN18iaWI,15109
+pygame/docs/generated/_images/surfarray_scaleup.png,sha256=sdxQlmVoRhlF_ocD2ecre0q51Q2LLoCxNsJaI8O1NYI,67759
+pygame/docs/generated/_images/surfarray_soften.png,sha256=XNzAZzfLUqn-QIQ25TxB2ujoDZkPEIRI6QSDnjJMIk0,47540
+pygame/docs/generated/_images/surfarray_striped.png,sha256=iH7gLZhBu5aATV-vfrsQmW30KdsMQoYB_LD-efRzl_Y,392
+pygame/docs/generated/_images/surfarray_xfade.png,sha256=uD8g8Ueqc3IMZaOqiD4n4sZmg5I4j798oIzl1pWDM70,41834
+pygame/docs/generated/_images/tom_basic.png,sha256=RzKBFmep1ksfD5QrJVW7JzdHvDN1_4ayUfGyVN-8Wms,5139
+pygame/docs/generated/_images/tom_event-flowchart.png,sha256=sG8YOH8YX2yTtx4-agBUWIcT8-jj1m6uKCF98QxmbKQ,5528
+pygame/docs/generated/_images/tom_formulae.png,sha256=6k8VDsueGVOh01ZrAVLE5miliOWKhQJqIrlPW_JEmXk,6763
+pygame/docs/generated/_images/tom_radians.png,sha256=BkBTx4OoiSXO5d6sMG01MyYBtO7EmDvgJixbTRKMm9Q,17409
+pygame/docs/generated/_sources/c_api.rst.txt,sha256=vSJF6tvDzMDdGTnXyyAi-Zz-iApxeuO-n5jTvceAQNw,473
+pygame/docs/generated/_sources/filepaths.rst.txt,sha256=sou-1N5amW1JXHqwKUU0BqYQmm2c5qDjjFKVIKIppfo,899
+pygame/docs/generated/_sources/index.rst.txt,sha256=UMDM3eMRAtTSwIiQfMXkaCKNWW6dlsnqR6MpxfGAMyI,5965
+pygame/docs/generated/_sources/logos.rst.txt,sha256=oJNZMoKbfJ9EuXTeipAahQGSpPkTaAcyHEIGWJPdWW8,1337
+pygame/docs/generated/_sources/ref/bufferproxy.rst.txt,sha256=V5gSzq__85alqL5Is05MTmpDv5NUHbLuuW4PYH98MzI,4708
+pygame/docs/generated/_sources/ref/camera.rst.txt,sha256=VGSdYbW2ii37WG3kC8HGn2Ao-TPRPYXTLez9VnwI364,9628
+pygame/docs/generated/_sources/ref/cdrom.rst.txt,sha256=0FlYODuxoQOsTYpZ9EiDYmUD9PIQrAeAlsXE8I67RQk,9068
+pygame/docs/generated/_sources/ref/color.rst.txt,sha256=DbdHOccIvmf8D8ERvo-fHKKvqKmZYb3tcNbQkSh_dj8,10798
+pygame/docs/generated/_sources/ref/color_list.rst.txt,sha256=XLIKLmTx_liiWk0gdKlrElBlCNZD0_sIvAg_El6tgfE,96353
+pygame/docs/generated/_sources/ref/cursors.rst.txt,sha256=dRrEevF8Uaho0It82zk-f1ySA9Pa_y3kUhM4mEm-zTQ,9415
+pygame/docs/generated/_sources/ref/display.rst.txt,sha256=Vnh3egkzBw1odhLMoVwFrR_WZLodTbgR2X01K4BhAkk,29190
+pygame/docs/generated/_sources/ref/draw.rst.txt,sha256=O-i4wVe9KULlpBvhdKrKbAYXiszO2_FUDhjzoEArmI0,24657
+pygame/docs/generated/_sources/ref/event.rst.txt,sha256=_0wgRCiHprZux0vJSF4wuyq0nAo1iOuK5T5eF1jzwTM,22211
+pygame/docs/generated/_sources/ref/examples.rst.txt,sha256=044bT9v4pQYRZqmaAwrdAI_GV3HLiwRlWHKYturVbN4,14095
+pygame/docs/generated/_sources/ref/fastevent.rst.txt,sha256=6gyem0cRZ-syKMDcQ6qexC83dJMro1O9-XHY_k2_HNg,3545
+pygame/docs/generated/_sources/ref/font.rst.txt,sha256=AGiup5hzk_KnfOElwadQGmzkzG9XcRex2yq9EvsNB-o,18217
+pygame/docs/generated/_sources/ref/freetype.rst.txt,sha256=R5RnUREdR9b5NPNsPYYTthl_ExWUcmX7vclWFjeNpyM,31386
+pygame/docs/generated/_sources/ref/gfxdraw.rst.txt,sha256=bQYID2bOb6xETZoDKM0mRtaCU_QD48MjQmCV8lvEErE,21842
+pygame/docs/generated/_sources/ref/image.rst.txt,sha256=4NJf_cJ9VVHm7VD1MD4msBUvjytoaCTkB83W6vubfkE,13395
+pygame/docs/generated/_sources/ref/joystick.rst.txt,sha256=sXgDyfTcwBdasNxdzW9r0Xnesxo6iwz23OBYG6SB0ws,19389
+pygame/docs/generated/_sources/ref/key.rst.txt,sha256=vSngmd3fawSny-yD8ord6lUR4sryVa9B6XcIiq8LyVA,16327
+pygame/docs/generated/_sources/ref/locals.rst.txt,sha256=VZi8cE2ZlPei4WdzSsok0fU_BOgPhlOmjfTCTSvl0N8,1022
+pygame/docs/generated/_sources/ref/mask.rst.txt,sha256=RAVpZZNK2i-ltHlCbNZgFa80x2RAXc_4lYkPgAVoyjs,24220
+pygame/docs/generated/_sources/ref/math.rst.txt,sha256=YbCqnfzaJeI1k8AYI45w8rokZ_x9f2NdnkoK2njrv3k,38384
+pygame/docs/generated/_sources/ref/midi.rst.txt,sha256=jIJ5qOZNvlMtBH0TA2icWTSB4_7CcUmU6_y9uVhGiak,14358
+pygame/docs/generated/_sources/ref/mixer.rst.txt,sha256=OHZqbN6F8ARkFGLe0cXHpAnOVf_MeqdC1cp5OeWRlbQ,22459
+pygame/docs/generated/_sources/ref/mouse.rst.txt,sha256=pzAlscsACbo_6p1pbazcbUaJW2vEf1Zx--jwy3ktA3k,8129
+pygame/docs/generated/_sources/ref/music.rst.txt,sha256=lLimQ-C1JXLMGw-gIHIsZ7r0bbav7SPTovbCqiXuO5Q,9531
+pygame/docs/generated/_sources/ref/overlay.rst.txt,sha256=loD8HVw0KQnsaPPisw_Xe8yms59CEAbdsboXwhS7zqk,2659
+pygame/docs/generated/_sources/ref/pixelarray.rst.txt,sha256=p0HY0TLZFoZAayVW9FxW8t0G4-Cjblhfd07uTkXqJK0,10204
+pygame/docs/generated/_sources/ref/pixelcopy.rst.txt,sha256=SMb-VGMZSxBBWgAjsFg2ucaQp80KglnIo56AkS7O-vE,4531
+pygame/docs/generated/_sources/ref/pygame.rst.txt,sha256=mEu8Av5H35_dC0JNXu9q1W6xnwgwvRr8ywaxNUEOcu0,15129
+pygame/docs/generated/_sources/ref/rect.rst.txt,sha256=Qf24A1_9KRJgS1-L5xlHoRWKQbDI0bE4rViR8Zc1aBM,21147
+pygame/docs/generated/_sources/ref/scrap.rst.txt,sha256=eeN2xL1riH4ZKbpNJSAXVlG1Ew2JYn1B-JiSBmdtZ_Q,7988
+pygame/docs/generated/_sources/ref/sdl2_controller.rst.txt,sha256=8wI9cr_XTTZ0Kym7dtICQhivaI40sh_0_vy_C2EK4X4,9401
+pygame/docs/generated/_sources/ref/sdl2_video.rst.txt,sha256=1WSISwTtmvOX6n_wEzAFk5Yj7XgmeG5GdrPIZB8K6RQ,9117
+pygame/docs/generated/_sources/ref/sndarray.rst.txt,sha256=9N1u6w7AxTr943nJiSUoDyNi-8qE_tWJeVU0rVEQYi0,3260
+pygame/docs/generated/_sources/ref/sprite.rst.txt,sha256=2RfcFwVV0xc3duWp_n_sVJSPFa9U_SOZKRb0AkTIGOM,30575
+pygame/docs/generated/_sources/ref/surface.rst.txt,sha256=eNBOZq2pkRHQcVtErIZs8jiG-nhAXSq3CR-Bup7_OQ8,36859
+pygame/docs/generated/_sources/ref/surfarray.rst.txt,sha256=rBeiZncxG9HIetGkZS0IcwxIlHqQgCxpiZxO6bSvY8s,12251
+pygame/docs/generated/_sources/ref/tests.rst.txt,sha256=vzkEPNYlI2274wXW3FKI1c9Qqmo293E7_eZhIIV9Xs0,4636
+pygame/docs/generated/_sources/ref/time.rst.txt,sha256=WJTVcpIu2GT39OD3k5MYQ3gXTqQJwJJaiNjA7GvWMlk,5624
+pygame/docs/generated/_sources/ref/touch.rst.txt,sha256=v7V0P85KlxiQIBLjaVyhTtH1fmDmqif-kDLhV0O6x4s,1957
+pygame/docs/generated/_sources/ref/transform.rst.txt,sha256=iZRvH-iMBYCszGMcoMQhut5OQDrGo07ElfF7jtX_mdM,13055
+pygame/docs/generated/_static/basic.css,sha256=sAj59T-GAN18hejRlkVoHGWW1U4oam_yVWMgFt5P4xc,15597
+pygame/docs/generated/_static/doctools.js,sha256=tcrUIItYleYYKj1roqKMOLpMPtfd_0Y1g5qkMO7llhQ,10766
+pygame/docs/generated/_static/documentation_options.js,sha256=tyP9OL3RCgebL7m7OJFenaW4w7PdYhH0PKEkEEvzE08,435
+pygame/docs/generated/_static/file.png,sha256=XEvJoWrr84xLlQ9ZuOUByjZJUyjLnrYiIYvOkGSjXj4,286
+pygame/docs/generated/_static/jquery-3.5.1.js,sha256=QWo7LDvxbWT2tbbQ97B53yJnYU3WhH_C8ycbRAkjPDc,287630
+pygame/docs/generated/_static/jquery.js,sha256=9_aliU8dGd2tb6OSsuzixeV4y_faTqgFtohetphbbj0,89476
+pygame/docs/generated/_static/language_data.js,sha256=JUzCtS3qbjtQkX7mhfWeiEGT3a8lHfhiLzC_G3YxgnU,11151
+pygame/docs/generated/_static/legacy_logos.zip,sha256=69C68jO62qau7eQx3z6U9pLnDhAMXK72RZ4PatlSMdY,51315
+pygame/docs/generated/_static/minus.png,sha256=R-f8UNs2mfHKQc6aL_ogLADF0dUYDFX2K6hZsb1swAg,90
+pygame/docs/generated/_static/plus.png,sha256=VBFRmblqEwy6AhR8R8DetD3Mm58ItRYruoZCs0mArGM,90
+pygame/docs/generated/_static/pygame.css,sha256=RTUqrXsr09Rdj6xtpM2Y_RZK90S1zXOpOAo2TV-0Gho,12699
+pygame/docs/generated/_static/pygame.ico,sha256=YeIWletq938Rg_G11m_1iGL_zw9T_U1QXZhjnbHjJSU,1078
+pygame/docs/generated/_static/pygame_lofi.png,sha256=QBECBUalJHRExXhf7nt-QpsVcu9LQ3RSAYVVS04AX3M,134242
+pygame/docs/generated/_static/pygame_lofi.svg,sha256=5eTaA6Alehzg0SuUSEFa7uONJe-0WHthEXNKPj75j5o,59281
+pygame/docs/generated/_static/pygame_logo.png,sha256=Jc1Lz47pY3mHjEFNzQXV9gx-olUUYCGuFYrutncaTXE,132068
+pygame/docs/generated/_static/pygame_logo.svg,sha256=3Oxb-IF0TZVcfuYPz-bHbd-OYLNLkTVuxiY1vWAmHUk,59262
+pygame/docs/generated/_static/pygame_powered.png,sha256=LgbswFcg647alSC5SawDPH96xuTMcWmMqVA6Zvs2K3Q,179911
+pygame/docs/generated/_static/pygame_powered.svg,sha256=1_0Cxa_PMRGgGvd2KktrKYSajaxGH_3FKaNWIpxeUbU,102819
+pygame/docs/generated/_static/pygame_powered_lowres.png,sha256=9go5WMiAE4fTEmB9w-zatEfROGl1IUWJGBpKoLqy-wU,179911
+pygame/docs/generated/_static/pygame_tiny.png,sha256=BXPk3OkSWdSkqjMkCQ1Dt5WjxZfb4zj4c2ir9U9_Y7Q,15310
+pygame/docs/generated/_static/pygments.css,sha256=85BWybvZ71cAlI5uqwNERzofjlACIqPlYvQDmkKVM2o,4919
+pygame/docs/generated/_static/reset.css,sha256=wqvSs8L_cB2K6bR903cOJkEtfJi0LpNQFl3nC_kZQr4,1083
+pygame/docs/generated/_static/searchtools.js,sha256=1rXuIe3XtGwCnFERMmcZ3OxcX1I2hwSpOy1khcsiQUw,16634
+pygame/docs/generated/_static/tooltip.css,sha256=UkuHG9X2M7DaTSMJX3-mW-4LV8wBtFxfG5k_pr7xrc4,798
+pygame/docs/generated/_static/underscore-1.13.1.js,sha256=zBD3mc0Pa2X5XEASRFSX5bo8ufUZZKlGiUCye96YtIc,68420
+pygame/docs/generated/_static/underscore.js,sha256=IY-xwfxy6a9rhm9DC-Kmf6N2OStNsvTb8ydyZxtq5Vw,19530
+pygame/docs/generated/c_api.html,sha256=8coHCHwmQDNR5PpRtRo7pfY9t8qwV4gqMRY_KndG0PU,7507
+pygame/docs/generated/c_api/base.html,sha256=PRtqzsz9MVqBTZ_XNySMb7zC4FCvpcHyLkScorf-_EQ,32451
+pygame/docs/generated/c_api/bufferproxy.html,sha256=ssNbOizMQNZpiKNacpE5FrlF9awXEx1axdGvpM2cJ4Q,12006
+pygame/docs/generated/c_api/color.html,sha256=hgXgBXCtMM_5VhlGIoLFy3KuDaykcQU2cnA7V6BCx54,10457
+pygame/docs/generated/c_api/display.html,sha256=oVZ3rja2N0NuNT2qevnUj0hmnn9Tl_7TjkXY82M8Kps,10934
+pygame/docs/generated/c_api/event.html,sha256=Yen7R7QmeR9u3LYtdr5tWdNKaSpH6vw9EoYyhrNAwCk,11914
+pygame/docs/generated/c_api/freetype.html,sha256=f1gqf95VYDFoCJPD_ZlAjSfM9-hER7dOISE5faHgSE8,11380
+pygame/docs/generated/c_api/mixer.html,sha256=RZLtL_Yhxl4iU4jTE8ohMzYl1ImBebnUs3bj8s5-EQM,15384
+pygame/docs/generated/c_api/rect.html,sha256=KF3mGaSyR5kDcm_IJTXR2gWURwI7HIUZ-cWQUDLrTnc,13874
+pygame/docs/generated/c_api/rwobject.html,sha256=2LBEh9ad6FodbAnaAv2praqFgOBxIkqtnznLe4L3ba4,15297
+pygame/docs/generated/c_api/slots.html,sha256=8n9EJ-rT8OupZAP0vR7JZPyUta8t2UgaNLwQH7Q2Go4,6823
+pygame/docs/generated/c_api/surface.html,sha256=O_emn82ZiVmrTmejZYEbMBK9elBNzb1MQQhNWzYOq24,14914
+pygame/docs/generated/c_api/surflock.html,sha256=1ktd_26-TI5V26yMBdgZM0wCr4zHARakTZB-BGPQLQg,16816
+pygame/docs/generated/c_api/version.html,sha256=N22Kce03eSq5vNKxkCyTdHOmwGiXTZ4B_uycQ6ZQJHg,8229
+pygame/docs/generated/filepaths.html,sha256=aUVG_q9n_rI7MOnPbg8QDjyo1-Hj8fX2iVl2bfWCb9M,6490
+pygame/docs/generated/genindex.html,sha256=9rB5dLKkvQtyyf-mHDRqQEHAsbT5gpPqPk_U_gkZ2qU,124089
+pygame/docs/generated/index.html,sha256=qyk-Y3FR6RPf2stn2w0iRU--1SfT7Ipl8hot15RQ4h0,24861
+pygame/docs/generated/logos.html,sha256=1DLRqlTAGMJwbaaej6ZAHnWj2kP7BqqZVqqn6KnqdZ0,7536
+pygame/docs/generated/py-modindex.html,sha256=EOxpsN6KqknHQAR4XStMh3i_vmYZIKdFc4jqEu5jy3k,11506
+pygame/docs/generated/ref/bufferproxy.html,sha256=vEyZAJpzy9dcc67VihI7K696mN-k2EW9VTaUkSJBEDA,17771
+pygame/docs/generated/ref/camera.html,sha256=PubUdbpaJUBTRnaPFjGykO78toSOwqHM5RLaBBT0FaY,27392
+pygame/docs/generated/ref/cdrom.html,sha256=A6PmC3hnjvNoHL7fx712bOg0hRCem4diCeVPExxNGxc,33410
+pygame/docs/generated/ref/color.html,sha256=WVMxogTDP7qqMa8TgTd1HuaRhLgfbbrD-Pxjd9SNrbk,36793
+pygame/docs/generated/ref/color_list.html,sha256=t95LHGAbAsRzisOkkEVzP7TCezxbDrZmqwMkpISYfD4,177095
+pygame/docs/generated/ref/cursors.html,sha256=9HZX58pnQEiX7ajKv0VTvrHdPQ9T0CQrUXF0b9m4l5Q,35588
+pygame/docs/generated/ref/display.html,sha256=I4pwizeY5oj_TW7Cw21NVFNS4aAsVrXOFX1yAiG84f4,77425
+pygame/docs/generated/ref/draw.html,sha256=Os3iS6SR77jn0FBWkeKXCavta7Wz5AAoeF94OH9JCYI,83998
+pygame/docs/generated/ref/event.html,sha256=ZCsHUpEozdLVkouqtutb4OIoGm7uYslagzhD-lGoluE,70340
+pygame/docs/generated/ref/examples.html,sha256=EgdeAVuocgVXmWRFKJL5BuImibKy-k4GXHJmA3SjE6k,48573
+pygame/docs/generated/ref/fastevent.html,sha256=tljyp2huAe1_8fPlsdintv2w71JDoYzf7GXxhd_ycY8,15114
+pygame/docs/generated/ref/font.html,sha256=ElhG9Kt-1ezdktewzUODYO-1NAgfhYe_kWyOnZs9axk,50015
+pygame/docs/generated/ref/freetype.html,sha256=llVWXTZXAiiCzO1UE7KdtPwDImneJMZzWy0NeEQE6NI,100747
+pygame/docs/generated/ref/gfxdraw.html,sha256=ju4C2Te9k3Hhnj2hfVGL6cWI9mKlZB-QtH-kkuAgl5Q,79822
+pygame/docs/generated/ref/image.html,sha256=wHp0ZI2-p1kC2nk_9ErhsJwUsGDltG-c3JKHOjVqYu8,40443
+pygame/docs/generated/ref/joystick.html,sha256=VzZCJPtbPt_pENszwxpbBed3LMLh22A8WzcaWt1_Zfk,96521
+pygame/docs/generated/ref/key.html,sha256=HSNX1SstfvGO9asWK3E4C0XALQlo0J2QbhintQD2uLs,42717
+pygame/docs/generated/ref/locals.html,sha256=9_NSEXvxLoYpwXzeGijFKqjZ24f7wU2QT6AHQx078Y8,8633
+pygame/docs/generated/ref/mask.html,sha256=QFl67JWyH-cgPJBTBtJYrXmOckESkYsq8D-4UhxWyhI,80499
+pygame/docs/generated/ref/math.html,sha256=wp6QzdEE7mHIesBv0Hq7LrE2TJWzQFgk-McEcHJLogE,123868
+pygame/docs/generated/ref/midi.html,sha256=4BrazKm37S0wtlJnuU1u0fjAiQxndraTlAcuu67pV8Y,52611
+pygame/docs/generated/ref/mixer.html,sha256=sS03x53pyYvixwvl89WRLbvbO5bY4Gg3xo0bxMVR7qc,64149
+pygame/docs/generated/ref/mouse.html,sha256=dr3viGiTdUHJIiumIEkCW2rKh8mTbKDMWNmQJt6htJ8,27506
+pygame/docs/generated/ref/music.html,sha256=U27g19ceOeWguNBg7X76e5Ekasc900BGIy9yv1looNg,33651
+pygame/docs/generated/ref/overlay.html,sha256=24evT1lPj5hSqqTzbPKPZzg_-Y5aNn0DZB_R77SuhHo,11027
+pygame/docs/generated/ref/pixelarray.html,sha256=2P6dxyjpqhAwEXEdTHgEm740GYubAuSmP_YbIe9q0zE,31490
+pygame/docs/generated/ref/pixelcopy.html,sha256=91M900OS2JtwmRuDxZ5Ex9_HPCvHubx--gjIgLYtGfE,15134
+pygame/docs/generated/ref/pygame.html,sha256=kTizk86vaGBanQyJ4KEKUSzYFyShLq5NQ7l6N5MXu3Y,50121
+pygame/docs/generated/ref/rect.html,sha256=EZMUwILGNUqEu1k7uachHbcucPxIffwfSmfNFbzLs2s,70063
+pygame/docs/generated/ref/scrap.html,sha256=V55CZ4qaW2wSAsVXFd90rEtUiy-IPUiFcXdK9sJEvGE,30582
+pygame/docs/generated/ref/sdl2_controller.html,sha256=jEDdXLANSmojvYdyzznfFd-DxlfAGlQBK5OTnwRLrAc,36874
+pygame/docs/generated/ref/sdl2_video.html,sha256=_eMPe52-cQCYHEW_dCm3adBGPImXM4kxpjiWbZYh_KA,61994
+pygame/docs/generated/ref/sndarray.html,sha256=J31OVRP0E2etH6sWntnbSRe22UcEbyySp6hww_MTA8k,14735
+pygame/docs/generated/ref/sprite.html,sha256=RXiS-P6IWmhfcmHAOYKPjGXrogoa6MFNrTBKOrvDNV8,96216
+pygame/docs/generated/ref/surface.html,sha256=8PqVHlynD77jzkGGmNo1g3WlI6jIe_6OIwOgKtxpTe0,89988
+pygame/docs/generated/ref/surfarray.html,sha256=4C1jPEnOvBuKK9K6SpySUwQp2Qtf45VRp1c5IQwztNI,38336
+pygame/docs/generated/ref/tests.html,sha256=300EcKiUdeXOHHw1lb1V3drHG6WjklYnyYFB3Sd529s,18592
+pygame/docs/generated/ref/time.html,sha256=TXwW69sXxky47zykj-ROPZc7OMItzOcbp5xfY4ROd-4,20060
+pygame/docs/generated/ref/touch.html,sha256=yFBWrQIxlAMcb6yP1KdBO3vadlWnlHf-2fWrqWEflH8,13013
+pygame/docs/generated/ref/transform.html,sha256=zuFY0gXBSDwlhPccx6zkMhPZbg8T30z3Sy_sqgB4iQI,42958
+pygame/docs/generated/search.html,sha256=hTjD-HvY02xD_aoaGfBwlpbtdCEXsqCCbm5pr03OIwU,3238
+pygame/docs/generated/searchindex.js,sha256=mGsUZXgrXxTsro7ZbK0iydSphgtKR72x4xRUUzemWLU,204820
+pygame/docs/generated/tut/CameraIntro.html,sha256=8U8pj6fCQ4wwZNTXP-VMDdtkC7BtIIPWNfmSj0E5qDI,38465
+pygame/docs/generated/tut/ChimpLineByLine.html,sha256=8adynePcZakKoLjqYCXxZ8mpELflOylzlOv9-w6x_2w,58949
+pygame/docs/generated/tut/DisplayModes.html,sha256=HPolOtYD6aTpYURzi0XgWi5b4fY9FULit4aD18zECbo,23420
+pygame/docs/generated/tut/ImportInit.html,sha256=uznuFJ_RG05GeB2Nzi74EvRICWSkuleooLTactmUb2E,9766
+pygame/docs/generated/tut/MakeGames.html,sha256=i_DqhG02gVAX1EDRaM9gYxO9difPT_4Uqw7jAd5Kwxg,14944
+pygame/docs/generated/tut/MoveIt.html,sha256=5Ac2zrAVLyIHZXTH8DauSWgqDunTuUVuNkXe_gaIq6Q,67457
+pygame/docs/generated/tut/PygameIntro.html,sha256=kYXXDGTrXOtpBeEPXHaQ1PosAaoRzDzitQMGnireJiI,29378
+pygame/docs/generated/tut/SpriteIntro.html,sha256=3FO6mIPTHeFWnbamGh53jdlpt5o4bNHuPWPc1UGL5v4,44630
+pygame/docs/generated/tut/SurfarrayIntro.html,sha256=eyCbnScwymNfEH1k0xRs01OKzq7cyipbfrKjL8B-OZY,51111
+pygame/docs/generated/tut/chimp.py.html,sha256=tU9PD4BwAUg2B8IhBccSRulf8OkGr7Q1rPmR1ybatSI,36492
+pygame/docs/generated/tut/newbieguide.html,sha256=JOfMz2TetldOl7G1yjYAgJ8sIEJOgZVYwR4WwiZG2dw,46008
+pygame/docs/generated/tut/tom_games2.html,sha256=91ZmYNb_dqx-M4vN-7k7X2upupPwqrX9Y999B8zcWWM,17798
+pygame/docs/generated/tut/tom_games3.html,sha256=V6IO3zHcR8j6Msqqm_btnIM4LipHkR7kJr9xTJbcMFc,15410
+pygame/docs/generated/tut/tom_games4.html,sha256=Yq18mTMBwLLObcydZnHOB02eHFBY9TdpifIhnxKpXAg,19305
+pygame/docs/generated/tut/tom_games5.html,sha256=2SLVRx4HtYnGRfhQkbxnI7Mtsn9crqvYCjFxmC9sBx0,21566
+pygame/docs/generated/tut/tom_games6.html,sha256=YU1Zyoo3iJsIQqMSRH87h-PlcfY7IJECEtgIko_fHsM,53335
+pygame/draw.cp36-win_amd64.pyd,sha256=xBbEC08GvNOWwHmcEX_hst-1tv9Yz4jud9QNNsnx7LM,50176
+pygame/draw.pyi,sha256=VtcYPysR7rBEPcB8XZrTmkvgvBZDoLPKI6mwhFIWubM,1694
+pygame/draw_py.py,sha256=ME-6PCJ7TRsIYoExnwgvEwj3iGTFPgjkNepQag8Nh1g,18662
+pygame/event.cp36-win_amd64.pyd,sha256=mUnxEsJyRM-N8P1wETEy46Pcpvimr3XHHLG3T-QZiEM,44032
+pygame/event.pyi,sha256=4v_0k2oj2RLibGr_hg5585mvi9CHaAB1tN7wN286Db0,1488
+pygame/examples/README.rst,sha256=K1xE9Fz9XWB05ZKpu4b96ycF19_DY5a3Y-Plv-oeqww,4174
+pygame/examples/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pygame/examples/__pycache__/__init__.cpython-36.pyc,,
+pygame/examples/__pycache__/aacircle.cpython-36.pyc,,
+pygame/examples/__pycache__/aliens.cpython-36.pyc,,
+pygame/examples/__pycache__/arraydemo.cpython-36.pyc,,
+pygame/examples/__pycache__/audiocapture.cpython-36.pyc,,
+pygame/examples/__pycache__/blend_fill.cpython-36.pyc,,
+pygame/examples/__pycache__/blit_blends.cpython-36.pyc,,
+pygame/examples/__pycache__/camera.cpython-36.pyc,,
+pygame/examples/__pycache__/chimp.cpython-36.pyc,,
+pygame/examples/__pycache__/cursors.cpython-36.pyc,,
+pygame/examples/__pycache__/dropevent.cpython-36.pyc,,
+pygame/examples/__pycache__/eventlist.cpython-36.pyc,,
+pygame/examples/__pycache__/font_viewer.cpython-36.pyc,,
+pygame/examples/__pycache__/fonty.cpython-36.pyc,,
+pygame/examples/__pycache__/freetype_misc.cpython-36.pyc,,
+pygame/examples/__pycache__/glcube.cpython-36.pyc,,
+pygame/examples/__pycache__/go_over_there.cpython-36.pyc,,
+pygame/examples/__pycache__/grid.cpython-36.pyc,,
+pygame/examples/__pycache__/headless_no_windows_needed.cpython-36.pyc,,
+pygame/examples/__pycache__/joystick.cpython-36.pyc,,
+pygame/examples/__pycache__/liquid.cpython-36.pyc,,
+pygame/examples/__pycache__/mask.cpython-36.pyc,,
+pygame/examples/__pycache__/midi.cpython-36.pyc,,
+pygame/examples/__pycache__/moveit.cpython-36.pyc,,
+pygame/examples/__pycache__/music_drop_fade.cpython-36.pyc,,
+pygame/examples/__pycache__/pixelarray.cpython-36.pyc,,
+pygame/examples/__pycache__/playmus.cpython-36.pyc,,
+pygame/examples/__pycache__/resizing_new.cpython-36.pyc,,
+pygame/examples/__pycache__/scaletest.cpython-36.pyc,,
+pygame/examples/__pycache__/scrap_clipboard.cpython-36.pyc,,
+pygame/examples/__pycache__/scroll.cpython-36.pyc,,
+pygame/examples/__pycache__/setmodescale.cpython-36.pyc,,
+pygame/examples/__pycache__/sound.cpython-36.pyc,,
+pygame/examples/__pycache__/sound_array_demos.cpython-36.pyc,,
+pygame/examples/__pycache__/sprite_texture.cpython-36.pyc,,
+pygame/examples/__pycache__/stars.cpython-36.pyc,,
+pygame/examples/__pycache__/testsprite.cpython-36.pyc,,
+pygame/examples/__pycache__/textinput.cpython-36.pyc,,
+pygame/examples/__pycache__/vgrade.cpython-36.pyc,,
+pygame/examples/__pycache__/video.cpython-36.pyc,,
+pygame/examples/aacircle.py,sha256=cKBEPuz4nVNgiyxf9SFI4c4P8jFmvJMHh_rlKDahm9I,1037
+pygame/examples/aliens.py,sha256=acRm3demyftDlt3uTc8_rI1xVlWqM4PZqt1ekxuYIs4,12310
+pygame/examples/arraydemo.py,sha256=G4ZYl8JqPNnbEJl6Ed7UCBfJvrz9QtHauFoFus87WB0,3255
+pygame/examples/audiocapture.py,sha256=hkq7mMKSmSvfHHD5UiIER4928il2l0mqdahkJViHHr8,1561
+pygame/examples/blend_fill.py,sha256=CCQJraeaBLEea-2_lWaUxVlcjM5rxy5UykRIjWCUAu0,3399
+pygame/examples/blit_blends.py,sha256=han3N9jK8dy57EePLJNJ73MEjfl34ypahoY7oGwZwt0,6318
+pygame/examples/camera.py,sha256=5205gt_W9EoTartrtQtXrdD-AkCLx6r8DkBaAvdiFO4,3905
+pygame/examples/chimp.py,sha256=2uBvczKQW8XEG87yJlf4CB46RR4b1a5iPjMi_C--fAY,5912
+pygame/examples/cursors.py,sha256=CtDYC49tEm2xcRft-Wlxa1zSVY3pqeunx0njhcCyV2c,7938
+pygame/examples/data/BGR.png,sha256=DvOrlW5BJdat94nNV8XEETBLRrSWRV7byQsMPsA69uw,244
+pygame/examples/data/alien1.gif,sha256=8Wveo1zpLVaFCtYITm_SoYqjy8L-TDuaZOcNa8Osqsw,3826
+pygame/examples/data/alien1.jpg,sha256=HOjXjmW4Ofsu_en9WNrkuIp_DCwupXcFB0Yt_cqV9rA,3103
+pygame/examples/data/alien1.png,sha256=femzLssV7oGvT3S2tyviyq7qO32QfhBDtMOR3ENBCLs,3522
+pygame/examples/data/alien2.gif,sha256=0MPpVYzvjAECy0pd7YRFKCEzzIYDKEJt70rbjlLbTZM,3834
+pygame/examples/data/alien2.png,sha256=FKGYDI2FBBR1Z56BLn357PNfh3-M38gAJpSQL8BpKYY,3526
+pygame/examples/data/alien3.gif,sha256=bFCRGZOQPaadCKIc-tlqoUjHdsi5IzR0E-2SjpPEvmA,3829
+pygame/examples/data/alien3.png,sha256=a51Tb9E4IvoICGzQChHq51RKVQJLf1GOCEeqA5yYfnk,3518
+pygame/examples/data/arraydemo.bmp,sha256=xM4-n_hRCQFZlfwwdTK6eaBweycUc863TgSFbWp3dbA,76854
+pygame/examples/data/asprite.bmp,sha256=97XMpKq9lLpMuv8UveCf8UJEAxheBhPUjHfMRQBkUx4,578
+pygame/examples/data/background.gif,sha256=-3kZwt99MFUBbBo-kHvPZXVlFrSB34XVNQWWxfHb970,9133
+pygame/examples/data/black.ppm,sha256=Yu8BwDOeFwOnVYjdWTMo7Tl1xcx2a7J38zZP-JllcMQ,6203
+pygame/examples/data/blue.gif,sha256=hqbgDzCeUz0NHjAQHYURIxSOpRbpHf6QeFch8ux_dAE,84
+pygame/examples/data/blue.mpg,sha256=XDj1CRPt1MWxspCfA3oqb822nlZgQ7CyyEuVJwlgmpg,6144
+pygame/examples/data/bomb.gif,sha256=TZ60QP1S2QBN6QPNSqBwS5VyebZA93iu8ZMUXzEg2QA,1170
+pygame/examples/data/boom.wav,sha256=kfoWs0VVDGHv0JSa46nXZBGyw70-jpfPq_B31qNA_F8,12562
+pygame/examples/data/brick.png,sha256=K_mshK0aL81nzOjAorTXyPps6n9mvofLeOWFXFpVjYA,170
+pygame/examples/data/car_door.wav,sha256=TwYWVqme5NqVVID1N4es92RSKEdTYkxbNx6dNamK-_4,3910
+pygame/examples/data/chimp.png,sha256=gFY5lDOflZ5fCMXpL9_HmipP4-3ALn_r6cCB9yTZKBk,826
+pygame/examples/data/city.png,sha256=c0Nu2o7x7QmvGMDmDCaPnhvJ8tPNuguKKpI_Z-NfQ40,143
+pygame/examples/data/crimson.pnm,sha256=o9ziiY4ox_cCmEo07w08SQckCQTRttxtLgKBE0VmZY8,3124
+pygame/examples/data/cursor.png,sha256=3RDqIuKTXH8Bs67n_ZwEbuS09dQtJeKRgSMR6D9gtWQ,2708
+pygame/examples/data/danger.gif,sha256=m0CBKalFbkqlohgOmrwkwVOfqBhRWonb7xm1pzbDy2Q,2761
+pygame/examples/data/explosion1.gif,sha256=WYcdwbZqmYdaaaPYFiR5vka0Anp4F4nnNlpSSx_1xug,6513
+pygame/examples/data/fist.png,sha256=X0VOsy6fP0UGqBjy7baoBX8XAXyp_1_s2tOItbtA7EI,86196
+pygame/examples/data/green.pcx,sha256=si9WT7dyn3nsXoh34UBW0yOCKWbC-Rz0fKkc_7TDRbY,320
+pygame/examples/data/grey.pgm,sha256=uWTtnBH-Fv605OtEJzS9fG5ns9XaeUHq2YeAC_cdkKU,4155
+pygame/examples/data/house_lo.mp3,sha256=R0nZUXymMp_XLPU8S1yvsiVeWT6MKLt5Rjp-WSnVrLQ,116320
+pygame/examples/data/house_lo.ogg,sha256=64FiQ1Zjq-cOj6Bmya_v3ZjEWmBaGZlTl19udKaz6sU,31334
+pygame/examples/data/house_lo.wav,sha256=B1BwfFaPIsSxaash-igVI_YE9SQd1BCXRTnSAKsNunY,78464
+pygame/examples/data/laplacian.png,sha256=uWI8dPstqMEPVuFPGtm-guu48T2-L3kn99rWA3ZhZ-Q,253
+pygame/examples/data/liquid.bmp,sha256=qtzPXhq0dr2ORNCCZ6gY2loT2Tsu0Dx5YvXB548I1Xg,11734
+pygame/examples/data/midikeys.png,sha256=9HCCmMHvlubR6G9a0jMv1C-AKeBzYfb5jjNhol2Mdqw,19666
+pygame/examples/data/player1.gif,sha256=3ZTVWGxnedKqtf3R-X1omPC0Y8jUSPGgHBAzeGhnV4c,3470
+pygame/examples/data/punch.wav,sha256=A0F1xT8aIZ6aNI_5McMqLygb1EfmdIzPi4kWkU4EwQc,4176
+pygame/examples/data/purple.xpm,sha256=3r6_3v6tob2qy-1hrQ3ujYHpuFb9UQ7LuNsHWq9mj5A,1249
+pygame/examples/data/red.jpg,sha256=mgaTBGP_k55FcqJIL7eV4jYll80zaZHPHfFtXAOLnF8,1251
+pygame/examples/data/sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088
+pygame/examples/data/scarlet.webp,sha256=iLN1RrY8LCSUnDrwYvWC99v_pLGy0iN8winH7VAyVL0,82
+pygame/examples/data/secosmic_lo.wav,sha256=-EIFkzj7k5qEqG04n7mnUGUp1SsyCJ4n08TzPT600DY,18700
+pygame/examples/data/shot.gif,sha256=bF2eY629zQzvDu83AKpveSFhJq5G4QpOE98A0tvbPFI,129
+pygame/examples/data/static.png,sha256=Xe4wN80awt7nTNiLemoSNTEKlAbGFW7djNETP8IleNs,1202
+pygame/examples/data/teal.svg,sha256=nkksR3fo0NPwC9sVXQPrPR_QrvqRiUB1vC4I-K83dho,313
+pygame/examples/data/turquoise.tif,sha256=4OkIy6CDPMv77tRR_wA9ZHA6qZzG3pjZ-1m1mNB7bcI,1186
+pygame/examples/data/whiff.wav,sha256=FMWM3XnYtce6mHFXQCYPgzT-xu-Q4DJybZfpPjG8cpE,5850
+pygame/examples/data/yellow.tga,sha256=EhxUG3SMO6bbHxr4yFggnKrsC1mYZVq-L6znAsR3z8I,3116
+pygame/examples/dropevent.py,sha256=WMvQbhrHNuSsEyv2hcFr4K4Q83MbZCKtsFNpfuH_SDU,2187
+pygame/examples/eventlist.py,sha256=aPTb0B3DAGnuG76Bz6l8aj6-r3VQ314o9LAiJEl2VHM,5912
+pygame/examples/font_viewer.py,sha256=5l9vYH-P4CTCBxM7TkYh9ASLhdSTf2NB7n-vNu5ZY94,9648
+pygame/examples/fonty.py,sha256=qiYuIracT_jwH5HFx9-tLcwc8qlgALwmL93aZjcUQrU,2073
+pygame/examples/freetype_misc.py,sha256=Fd3USUExyXsmQWWO9I8f2TMLXDooCAebYx10Pnnmbxk,3659
+pygame/examples/glcube.py,sha256=UONh_9RvLhCG8qLQUys4sE2xXY8ukf2zs0KXr_IRE5k,16860
+pygame/examples/go_over_there.py,sha256=YPlaHd2rd4Kc7Cm5VYzRjHUiF8eng04rTO-5cEC_hJ8,2150
+pygame/examples/grid.py,sha256=9dEYCiBjkNxjvk5aMukZxyGtaSVUIWXR4MCN56JEsYY,1745
+pygame/examples/headless_no_windows_needed.py,sha256=Lf-FVBNEHON53nSPLFR5DXHVua5S5N_4LX3gd_yRf_o,1299
+pygame/examples/joystick.py,sha256=pIw3_JRd9R0bpu9LPMiMuN53LVZliWehR3MJ5SFcmzo,5252
+pygame/examples/liquid.py,sha256=mGAniBgkpFYhNyM4XMoFRtlwCbQtd5aZaWCI6C23Bak,2544
+pygame/examples/mask.py,sha256=henO1A-xYDZrB6yqYVOnXkVr-8JeZbNXDODhGmczUgU,5725
+pygame/examples/midi.py,sha256=iQh1tPyyi7zejua6VJa--77aPW-i8Bb8TokRtfaHLRo,31267
+pygame/examples/moveit.py,sha256=cLYWPTnqRVud-Lbq9bVd2S1W8KA8eSExjXmFof35wdI,3330
+pygame/examples/music_drop_fade.py,sha256=bzhtDpDkjyod6Jb-rLU6KAh3KAiHQLFP1XVx717fPt0,9110
+pygame/examples/pixelarray.py,sha256=UQzu0tO7g8EOMoQPVLoFRUvMv1ePd9ybkGUg1PKYoXA,3453
+pygame/examples/playmus.py,sha256=xPhC5wCIyEOjiJkz-ZNtRUl9kladd6m0ZU_jl6QSEko,5215
+pygame/examples/resizing_new.py,sha256=8p6Sy8s74A49OXeboxmWnKGQJVO99ATE4nm7A_ADTxY,1046
+pygame/examples/scaletest.py,sha256=Iq8w-K4pf92-2oee7caNcW9KaOIosArD4AL6roR-svY,4826
+pygame/examples/scrap_clipboard.py,sha256=L2tOzkBSxV3f3hva8XiUibMmYW3oxEMlVUmnX97dXlI,3033
+pygame/examples/scroll.py,sha256=2mKc79QAHLWMO8mG50jmuWTXAvZXdtVsqwjKVFA18RY,6642
+pygame/examples/setmodescale.py,sha256=6GCUOLrGp7KITI1qoGj1hJqNxkouUqmdfYgXlD7IYns,1801
+pygame/examples/sound.py,sha256=CwQ3hSKjD_sHmXEBLfimdbt18cQcXwk5QQ7jfnOuSS0,1172
+pygame/examples/sound_array_demos.py,sha256=Pp6ZsH2WRn_T2z5JNSdpEwILbTh2t_60F5tvNL7zt_s,5756
+pygame/examples/sprite_texture.py,sha256=XCbP01L_tVSylh67kvnPfbU7jLVGXfCwLwZ-HnPa1rU,2667
+pygame/examples/stars.py,sha256=fB6OkpKUYh2WtI04nM32_8A5QDrrw8Xlg_ZCTmVfl6w,2714
+pygame/examples/testsprite.py,sha256=2SRjgFolTSWWxQroPm_HztVADxoKvg5JXddawjvjkjY,6862
+pygame/examples/textinput.py,sha256=vf887f8nAInO66p4qwZbJu316I2bWmUkmZHi50hu9TQ,7579
+pygame/examples/vgrade.py,sha256=RZDRrsAi0bTg6SAh8UJqRQx8viMwjmLSyK20dCql7us,3263
+pygame/examples/video.py,sha256=v_81PS92Mxad1KelrHOAOGwLsIjE46GIM7HwUj9oSPo,4357
+pygame/fastevent.py,sha256=NOVGX3eAvQGCSHDOZZJ_VuWSyekqdPO1Wrr2MvCEQeU,1694
+pygame/fastevent.pyi,sha256=3FmWstDsaRS5TOqExgceS8jeZnkkYhSrYIgTEqSKkU0,249
+pygame/font.cp36-win_amd64.pyd,sha256=8E-mqqILfl2J3qzcOhuVjfJgPMG2Klg5R_hVav_icvk,24064
+pygame/font.pyi,sha256=YDTjkLtJUC_xp4Na6q-tolDN1AiFk9WTioGtwNHWFPU,2079
+pygame/freesansbold.ttf,sha256=v5JRJp8R5LNVgqmTdglt7uPQxJc6RZy9l7C-vAH0QK0,98600
+pygame/freetype.dll,sha256=HEhzktbQaXC6PHtScFiB8fsGn2ByQ0mSdsLwwDPH328,654336
+pygame/freetype.py,sha256=OyLcjKlZXyhQEV2EZ0xMY57HypqGA77K14CJGMxw5H8,2224
+pygame/freetype.pyi,sha256=sjguJUovQqRjoKZ9xACq52-ykUY2XRir__Uody8UPVo,3512
+pygame/ftfont.py,sha256=wJeDvzh9XT3Mi3Yur-JUCIBICEsOeNpeXwW--eBoLbg,6153
+pygame/gfxdraw.cp36-win_amd64.pyd,sha256=xSVgXNvjtA4E6vmm38wgfmz3nWwGKkLfo_yW6p-1WmE,58880
+pygame/gfxdraw.pyi,sha256=GbHQUuqOdJAxe6tQfp1EfhL3-P6vBltbayaMBeFjsVY,2500
+pygame/image.cp36-win_amd64.pyd,sha256=hH9vSXnSILrDLdd0T8iOm8MauxyuwQ_bobk9CAPqgr0,29696
+pygame/image.pyi,sha256=5bCTCOTJ5azmeIV82settKwR4aKfOA_kB_1mSiBTqSs,1705
+pygame/imageext.cp36-win_amd64.pyd,sha256=aY80X6Ygt09lpCoxyOMjMvE8q25-L_vlrOxD5C2NVrE,17408
+pygame/joystick.cp36-win_amd64.pyd,sha256=0_QI3Zr3dCTIHhFwwvB--mwMFytPlS5EtUkTMMO5nGk,20480
+pygame/joystick.pyi,sha256=Lr0DiEuJjbuyGEc1fCPkzVMYPu_qPGUky_x-k5KikPQ,1348
+pygame/key.cp36-win_amd64.pyd,sha256=_XrtabSwKu9ez4V_61M4XozngFu3W0DgwckRrJR6I5o,20480
+pygame/key.pyi,sha256=7Ei4SZ3tuWMb9XAtRZm2lw9Sd5BDwjzM3QS1-WRrcBY,562
+pygame/libjpeg-9.dll,sha256=OiJK9UDJZXSAD16az2SyzfuQYOcnkZ7BT70Yeptb_mk,244224
+pygame/libmodplug-1.dll,sha256=DBqQMoEuxMIAA6mXQj5ntx7LXlnWLNwYpb9ZEXapAQ4,265216
+pygame/libogg-0.dll,sha256=V6vE9qmszdCL-aKwIqZmQMxialvU2sbHxPBqXfYe4f4,25600
+pygame/libopus-0.dll,sha256=dxyueUEPf8xPmToQWhjE7Z6Mvd1vgHpCIo2V9XWAiAY,368128
+pygame/libopusfile-0.dll,sha256=zKrKgYEL0tHKtGkrQlOmOfjVUWmW2w4k2IHv0-_cxqQ,46592
+pygame/libpng16-16.dll,sha256=5oi0pNGPS2zMmcbKSYD1EhjLglYQd1GS2bYLLwXv8tU,210944
+pygame/libtiff-5.dll,sha256=6_6XrF7ya5SUWvPbX_0RCkuOktwCVZv4HMsz8NXrzpU,432640
+pygame/libwebp-7.dll,sha256=mlNWO2BY9w8nJQKbfdL-lvhpwg6AkAMc0wPplN_ge1A,447488
+pygame/locals.py,sha256=IO2_D-d3Z5FStP6QDjiyN_W0QPq0i_hHuCQWhxdFRSs,1195
+pygame/locals.pyi,sha256=fLWVStrayTJQMr7r5z45ywHMIOgEYIqLYJkyjIcUsGg,9925
+pygame/macosx.py,sha256=GJYmXKulp9UCu5NgYMtHIzp22ij-QZBhNOLbrJdgtsM,329
+pygame/mask.cp36-win_amd64.pyd,sha256=opBMxmHUzrm5waEPjpXGB1QRUxgyB7293o0snvof0zw,55296
+pygame/mask.pyi,sha256=bBxSevY0teCHn86xPnQIgx10xLd5HX2O3Ohkfrz_m5E,2304
+pygame/math.cp36-win_amd64.pyd,sha256=zSxy02uIizENi3IjAAVtOYbjFOjdITYiQDgeDlDz408,76800
+pygame/math.pyi,sha256=crW5wZF1MKl2sERrePqGW1roEXACWAbyreqbi2n9zZs,11685
+pygame/midi.py,sha256=GEmQFXnCclms235CT5ULsMs6fIbnrIpwo57J-Kbla_c,24341
+pygame/midi.pyi,sha256=LlPPnnXyHdklV5OTipShPxi8WUcc-NyasfLYxz-fcGw,1774
+pygame/mixer.cp36-win_amd64.pyd,sha256=d9hP9oxHmhOIII5Bj5uznL3KFGAWzBrRjZ0VkdR0LUs,37888
+pygame/mixer.pyi,sha256=HuPeysk7q79eWlPe2r54Z-j1Kgx1JKTQZnvXCESibjA,2856
+pygame/mixer_music.cp36-win_amd64.pyd,sha256=uDecDQI4mJBREyvzPNipbW48FJ8HUrgvpy2_9g0CKDA,20992
+pygame/mixer_music.pyi,sha256=gMklc23g-bBIAGa3giyM00yi2jLedrKipx9_8lebvFY,691
+pygame/mouse.cp36-win_amd64.pyd,sha256=qySAtcB6snuc6osGIEJKAAjpocwBFPZksUTx1sKr1-Y,19456
+pygame/mouse.pyi,sha256=a9Y_UtpHhMVQqpE3LnkAaI3EoVjQMdVNJNVeZ-eXTXE,1184
+pygame/newbuffer.cp36-win_amd64.pyd,sha256=2g6pNLh0k8XpjIUfNuEo127nuGyACW01OidL2ZCBWgg,21504
+pygame/pixelarray.cp36-win_amd64.pyd,sha256=vyMbiJx0rm4Dch86EFB7k5nhOrC1-O9MSagsV6NEdsY,47616
+pygame/pixelarray.pyi,sha256=8bDvIdo9QPBfgSSU04kxZaYCkkdv_XyXRQtA28YI6P0,1226
+pygame/pixelcopy.cp36-win_amd64.pyd,sha256=V9APSiCOzPgTuQpPFRxojl-NX7u1NzvE-SGrgNTqRq4,27136
+pygame/pixelcopy.pyi,sha256=9P6c4l_16qAfJYsQ3IkJGPMKyp_weLB1e9iQIvLYDZM,534
+pygame/pkgdata.py,sha256=RTPgP8uEaPK9GdyeOSUzdN4N9LLdrUASHUXMoUersFY,2421
+pygame/portmidi.dll,sha256=yfjZBDrBVwsQ8QTy0ArseR9WJhyE7kB3O-c9Cjgi4BM,41984
+pygame/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pygame/pygame.ico,sha256=PBF9cw0Ca9Rw2pNmDD3iXvcYYQeI9ZzZ9vxtRLQRoJc,145516
+pygame/pygame_icon.bmp,sha256=Twnby8nv4HMhGka49n-47CPseDvwrSLZ0l1o9U2Bb5s,630
+pygame/pygame_icon.icns,sha256=4jwAo9VtMhTK9hq9ibt6MZ4_sd2VsZueWZ3YAMuTPgw,264148
+pygame/pygame_icon_mac.bmp,sha256=QrAs3kEF6v7wVMlIJgktI90aUdTg9RdTmp16d2HZhNg,262282
+pygame/pypm.cp36-win_amd64.pyd,sha256=27oPUBiMNj95yDcMeahvST8pEPexaOMoyyStngbjo7s,103936
+pygame/rect.cp36-win_amd64.pyd,sha256=ydjqM2clNsw8k7321dZYXoA_a78Ra0ROvM_zVHG_YHg,46080
+pygame/rect.pyi,sha256=gOICRGGXcziZKQvJrWVJ34H3wNl6-3Qp9uyWicbLHko,7138
+pygame/rwobject.cp36-win_amd64.pyd,sha256=I_FHmYJk_xSib71k9nXEXrfYs695qgmMYj5FyAgTqtw,19968
+pygame/rwobject.pyi,sha256=7pjMcSV8digIazj9i0h5se7steQNUtBBBcK-f2sY-Yo,544
+pygame/scrap.cp36-win_amd64.pyd,sha256=GVNFke5qe0c7f8isFP65VNddWfQbmZ81NoJM0bxIJMg,18944
+pygame/scrap.pyi,sha256=xEbe7P1WNSmqeFQ-M3XCX3JztjHu1-m_0x8vkL6S4bA,366
+pygame/sndarray.py,sha256=hJMTnQBEbOBYzeBRdCiGHCr3S20NqpHVevX2WFxw51I,4083
+pygame/sndarray.pyi,sha256=-kUmCShhKSazBRhHberq4kDhQau0I-02LZHvfIVYi7A,337
+pygame/sprite.py,sha256=Uaas5bn2a85gfbRXHGJwlQVh6OLA_vrPc8iK4qbTcqE,62899
+pygame/sprite.pyi,sha256=M6V64QuHdfITdWOaX6Xa20v4B9tJoUjYtyCdCcqz0LI,9748
+pygame/surface.cp36-win_amd64.pyd,sha256=DmMbde06AUcbC4PZN5WY9ya1BNtD4AdQ2-i_qDTVG34,239104
+pygame/surface.pyi,sha256=mKyduOgOgekHQ3JObqdjFMkTMI22Aeat2MCAkOTWseE,4726
+pygame/surfarray.py,sha256=xlKZVqyoL8DSOAGOarb0E8VSha0vPn3xYY2fJz7zT8o,14405
+pygame/surfarray.pyi,sha256=g4w8kePGEZGMGhdtKo0ClZ3G-VPitz7DmrtIK6a7FGY,1287
+pygame/surflock.cp36-win_amd64.pyd,sha256=QdWl_9P896mHsx8iCk4QsIWn6ABcAN9EPkYdkfcGsvI,13824
+pygame/surflock.pyi,sha256=p6HFejTvHw2HxSUOD4vW9HolANRfvGHjADdRh63skKQ,122
+pygame/sysfont.py,sha256=8-p9XqoPP15Vkmtm9WcIzi_xmpIvk-WobfExf4MWxsM,15391
+pygame/tests/__init__.py,sha256=wfUhz-LZF-OXZNT81UfGdofNYPCUMJoF3nwgXpzg4sE,1251
+pygame/tests/__main__.py,sha256=xLKWh5Kk0hc-2vz_OrNsMkVproZ3ndWz6ZCMY7Myk5A,3788
+pygame/tests/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/__pycache__/__main__.cpython-36.pyc,,
+pygame/tests/__pycache__/base_test.cpython-36.pyc,,
+pygame/tests/__pycache__/blit_test.cpython-36.pyc,,
+pygame/tests/__pycache__/bufferproxy_test.cpython-36.pyc,,
+pygame/tests/__pycache__/camera_test.cpython-36.pyc,,
+pygame/tests/__pycache__/color_test.cpython-36.pyc,,
+pygame/tests/__pycache__/constants_test.cpython-36.pyc,,
+pygame/tests/__pycache__/controller_test.cpython-36.pyc,,
+pygame/tests/__pycache__/cursors_test.cpython-36.pyc,,
+pygame/tests/__pycache__/display_test.cpython-36.pyc,,
+pygame/tests/__pycache__/docs_test.cpython-36.pyc,,
+pygame/tests/__pycache__/draw_test.cpython-36.pyc,,
+pygame/tests/__pycache__/event_test.cpython-36.pyc,,
+pygame/tests/__pycache__/font_test.cpython-36.pyc,,
+pygame/tests/__pycache__/freetype_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/freetype_test.cpython-36.pyc,,
+pygame/tests/__pycache__/ftfont_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/ftfont_test.cpython-36.pyc,,
+pygame/tests/__pycache__/gfxdraw_test.cpython-36.pyc,,
+pygame/tests/__pycache__/image__save_gl_surface_test.cpython-36.pyc,,
+pygame/tests/__pycache__/image_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/image_test.cpython-36.pyc,,
+pygame/tests/__pycache__/imageext_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/imageext_test.cpython-36.pyc,,
+pygame/tests/__pycache__/joystick_test.cpython-36.pyc,,
+pygame/tests/__pycache__/key_test.cpython-36.pyc,,
+pygame/tests/__pycache__/locals_test.cpython-36.pyc,,
+pygame/tests/__pycache__/mask_test.cpython-36.pyc,,
+pygame/tests/__pycache__/math_test.cpython-36.pyc,,
+pygame/tests/__pycache__/midi_test.cpython-36.pyc,,
+pygame/tests/__pycache__/mixer_music_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/mixer_music_test.cpython-36.pyc,,
+pygame/tests/__pycache__/mixer_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/mixer_test.cpython-36.pyc,,
+pygame/tests/__pycache__/mouse_test.cpython-36.pyc,,
+pygame/tests/__pycache__/pixelarray_test.cpython-36.pyc,,
+pygame/tests/__pycache__/pixelcopy_test.cpython-36.pyc,,
+pygame/tests/__pycache__/rect_test.cpython-36.pyc,,
+pygame/tests/__pycache__/rwobject_test.cpython-36.pyc,,
+pygame/tests/__pycache__/scrap_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/scrap_test.cpython-36.pyc,,
+pygame/tests/__pycache__/sndarray_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/sndarray_test.cpython-36.pyc,,
+pygame/tests/__pycache__/sprite_test.cpython-36.pyc,,
+pygame/tests/__pycache__/surface_test.cpython-36.pyc,,
+pygame/tests/__pycache__/surfarray_tags.cpython-36.pyc,,
+pygame/tests/__pycache__/surfarray_test.cpython-36.pyc,,
+pygame/tests/__pycache__/surflock_test.cpython-36.pyc,,
+pygame/tests/__pycache__/sysfont_test.cpython-36.pyc,,
+pygame/tests/__pycache__/threads_test.cpython-36.pyc,,
+pygame/tests/__pycache__/time_test.cpython-36.pyc,,
+pygame/tests/__pycache__/touch_test.cpython-36.pyc,,
+pygame/tests/__pycache__/transform_test.cpython-36.pyc,,
+pygame/tests/__pycache__/version_test.cpython-36.pyc,,
+pygame/tests/__pycache__/video_test.cpython-36.pyc,,
+pygame/tests/base_test.py,sha256=d6POJDqd_uxot9yxTxRBW2thiOD4o0HYuIHBq84OBUg,22449
+pygame/tests/blit_test.py,sha256=IUGAXjy0ZK72jmboE2r9LF9wZ7TXlYJAzm99a4JNfV0,6343
+pygame/tests/bufferproxy_test.py,sha256=bji75h8zDPEHNXSKEnT9Gb4T0_cMcAlDxqvPdlR5WU0,16451
+pygame/tests/camera_test.py,sha256=kMyJ3SYnifZe5Mv7JRl8exEuJ4IOe2SA98_6y-Uour0,801
+pygame/tests/color_test.py,sha256=I0OxIOf8sqYk8nW3Wmy76AqFuFSCwafDGUOF8O6QA58,49781
+pygame/tests/constants_test.py,sha256=kOOqPnXMMka37d4VUDo70j3i_zAN74Y5QuRLnNQsRao,9308
+pygame/tests/controller_test.py,sha256=a6NSWn1k1Rpt4HTYeOAbF-cDx0KfnKsrG6DwA0vk_0k,10834
+pygame/tests/cursors_test.py,sha256=qc-T5sdh25LO-KOK16CMp9FnWCXJSwdUo29YkePeSTs,7700
+pygame/tests/display_test.py,sha256=hSHKBJUqDJWzgnQgNten0L5ejEQgNd1wZRklzBDLzmo,46425
+pygame/tests/docs_test.py,sha256=r2qa_ox8eg2_Y5Pb9-XzZExAhqJrdVMO30guO1o7PPM,1091
+pygame/tests/draw_test.py,sha256=YEt_6zJKnTRBjxHbO4dKBfWwFOmGDVbhwPe0zvdt5zI,237912
+pygame/tests/event_test.py,sha256=XNd_lLqGbZ9PwAJPL4AxoZwblLTlQrsLU_KpoiMyaog,33593
+pygame/tests/fixtures/fonts/A_PyGameMono-8.png,sha256=QmhReADwKrzW5RWnG1KHEtZIqpVtwWzhXmydX1su10c,92
+pygame/tests/fixtures/fonts/PlayfairDisplaySemibold.ttf,sha256=4Q60pnYY-7dwBTVr7PBSz5r-w_kfUzhal8i85ISG7V4,236636
+pygame/tests/fixtures/fonts/PyGameMono-18-100dpi.bdf,sha256=nm3okxnfAFtADlp7s2AY43zS49NYg9jq7GVzG2lPhOQ,1947
+pygame/tests/fixtures/fonts/PyGameMono-18-75dpi.bdf,sha256=4kB0uYeEpa3W-ZAomFMpc0hD-h6FnOh2m5IPi6xzfds,1648
+pygame/tests/fixtures/fonts/PyGameMono-8.bdf,sha256=aK0KV-_osDPTPiA1BUCgZHOmufy6J9Vh5pf1IAi0_yg,1365
+pygame/tests/fixtures/fonts/PyGameMono.otf,sha256=_Af4LyMEgKKGa8jDlfik89axhLc3HoS8aG5JHWN5sZw,3128
+pygame/tests/fixtures/fonts/test_fixed.otf,sha256=FWHmFsQUobgtbm370Y5XJv1lAokTreGR5fo4tuw3Who,58464
+pygame/tests/fixtures/fonts/test_sans.ttf,sha256=nrZ6FRet4dwlvA7xOReYCP2QwyGebk0iVJaSFbtpOhM,133088
+pygame/tests/fixtures/fonts/u13079_PyGameMono-8.png,sha256=x_D28PW8aKed8ZHBK6AISEZ9vlEV76Whi770ItTuFVU,89
+pygame/tests/fixtures/xbm_cursors/white_sizing.xbm,sha256=VLAS1A417T-Vg6GMsmicUCYpOhvGsrgJJYUvdFYYteY,366
+pygame/tests/fixtures/xbm_cursors/white_sizing_mask.xbm,sha256=CKQeiOtlFoJdAts83UmTEeVk-3pxgJ9Wu2QJaCjzAQM,391
+pygame/tests/font_test.py,sha256=lYgOKil5siDjsy-0FV2kHOTO8vdAESmVzYOoT8yONyc,27182
+pygame/tests/freetype_tags.py,sha256=NdjMDSYHfrhopKR0JuTeUfFX-AbcCu4fsXnS1a46iVM,182
+pygame/tests/freetype_test.py,sha256=GFZUMLZF-KjXEyOhq0FqI2Oe_nt1hbkPHTkSW7RN3pk,64896
+pygame/tests/ftfont_tags.py,sha256=IvteBUDEp4rv9q6FwlTpQ9X2px-XUromSMQ921VrhCU,180
+pygame/tests/ftfont_test.py,sha256=YZesw8r_NfqcoUsg78FbAh0kJaeC6iAzixHMsQcH3gY,421
+pygame/tests/gfxdraw_test.py,sha256=XWxHvtvkMWllmlCsGhaUGCdOFBAiOfJgYg_KnUj8Q_E,32354
+pygame/tests/image__save_gl_surface_test.py,sha256=5H8TeGZNRZzu5kJInWPe8AuuKqHv-utunadoBmn--CI,1198
+pygame/tests/image_tags.py,sha256=_WJGXgTOaUn4IG7fIk1sDKfDDZP3W8N6PkrrOpPT-U8,132
+pygame/tests/image_test.py,sha256=LaHCxn5hpcy1s9SdOq712iPau_4p-2Gq0mKjmc9pifc,43024
+pygame/tests/imageext_tags.py,sha256=-vnXr7O5F1NVrEDrOHBEYdaD-JiuBT9NI-lxGps-K1U,135
+pygame/tests/imageext_test.py,sha256=Dam4nzQG1dZtJ8rtAmSw2xdhIvENATklN81mVl5Mh4I,2852
+pygame/tests/joystick_test.py,sha256=XArf2gXSYYupNOuOekpKtGlJ_vaLWWUSl7hpKnBE3FU,6078
+pygame/tests/key_test.py,sha256=rZ9EPqi8q6VQI7kQzdjW8U0Ebqk2QYnU9D7i2TXsdDY,9172
+pygame/tests/locals_test.py,sha256=2g4vCW-wJG0U_wA7VP1kieV-wSsvGInyGkLW6OjvJ4o,417
+pygame/tests/mask_test.py,sha256=95xNu2gy1TVgxKNnDdcYXgo_rKaTdKPAqeeKn1jI_Xg,245880
+pygame/tests/math_test.py,sha256=LmwS-6B6LU4EXRx9_I-s_WyrddmEcu7HDp7xNL6VK30,111540
+pygame/tests/midi_test.py,sha256=utmDFvk5wZCcXrTTGXnl4N6dMjmkXgrIV6nGtgcws6k,16905
+pygame/tests/mixer_music_tags.py,sha256=o0gsQDjuICFYw8j3WOlIluwk9fdA42ledU1U6DIJzNU,138
+pygame/tests/mixer_music_test.py,sha256=rgTeUXFm__7ZR5SDVqB6O5VyRlwFuTZ9LrSP1Q4-R3s,17522
+pygame/tests/mixer_tags.py,sha256=qKcn8AD46H3V87xONG0iXlGH_FveeGBgf2gE1MMh2s0,132
+pygame/tests/mixer_test.py,sha256=OMElH1sGbJGlRK7Psr1qwWZvHG_MciAA5F_6o9S6rsE,54019
+pygame/tests/mouse_test.py,sha256=wL5Jmwj65UhtrON5rSXImomjNE_afKGlFQqlS-JZfT8,13146
+pygame/tests/pixelarray_test.py,sha256=NKVn_4NumiMz0CHUDm7DH-OcEZI0XpAZmjenAXwDJlY,62745
+pygame/tests/pixelcopy_test.py,sha256=j8ywtiFayJMiRBi1v6ywED8xnRkpSVv5vZ579yEvpDA,25543
+pygame/tests/rect_test.py,sha256=qN1IQrf9iFXuJfIF-8sOwqouxbMJkzNPLTD-YvIxHlA,117687
+pygame/tests/run_tests__tests/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/__pycache__/run_tests__test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/all_ok/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__pycache__/fake_3_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__pycache__/fake_4_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__pycache__/fake_5_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__pycache__/fake_6_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__pycache__/no_assertions__ret_code_of_1__test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/__pycache__/zero_tests_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/all_ok/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/all_ok/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/all_ok/fake_4_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/all_ok/fake_5_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/all_ok/fake_6_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/all_ok/no_assertions__ret_code_of_1__test.py,sha256=PNrfACCpcPnO964Oxv2-9l4ciuJ-Iqw3x8HDs-kebVg,797
+pygame/tests/run_tests__tests/all_ok/zero_tests_test.py,sha256=XzLaMjkygsvNkFEqnRU9y2Ijm6bfds9n5Z6mg_LOMJQ,545
+pygame/tests/run_tests__tests/everything/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/everything/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/everything/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/everything/__pycache__/incomplete_todo_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/everything/__pycache__/magic_tag_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/everything/__pycache__/sleep_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/everything/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/everything/incomplete_todo_test.py,sha256=71myeZtFerYY2rB-j60l5Ltz3FiRCuOR4evFXtJHC34,909
+pygame/tests/run_tests__tests/everything/magic_tag_test.py,sha256=SjIKB_7aLfGdih8cotQ34m1KbSEII_1wGQUBwrWeIyY,859
+pygame/tests/run_tests__tests/everything/sleep_test.py,sha256=AyGwZk5fQAkfeCr9VewdsuD_z5BzlVfkmbZD-XetB50,715
+pygame/tests/run_tests__tests/exclude/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/exclude/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/exclude/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/exclude/__pycache__/invisible_tag_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/exclude/__pycache__/magic_tag_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/exclude/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/exclude/invisible_tag_test.py,sha256=AdHFvOK-kCRi2iUs68So6Ngef6C_LEdx3QpMLjhKtmM,925
+pygame/tests/run_tests__tests/exclude/magic_tag_test.py,sha256=SjIKB_7aLfGdih8cotQ34m1KbSEII_1wGQUBwrWeIyY,859
+pygame/tests/run_tests__tests/failures1/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/failures1/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/failures1/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/failures1/__pycache__/fake_3_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/failures1/__pycache__/fake_4_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/failures1/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/failures1/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/failures1/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949
+pygame/tests/run_tests__tests/incomplete/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/incomplete/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/incomplete/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/incomplete/__pycache__/fake_3_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/incomplete/fake_2_test.py,sha256=RVUuQZxqYScIUAflNIsXd7UE6Rxm6HHFZSi8cpz5m-k,889
+pygame/tests/run_tests__tests/incomplete/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/incomplete_todo/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/incomplete_todo/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/incomplete_todo/__pycache__/fake_3_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/incomplete_todo/fake_2_test.py,sha256=71myeZtFerYY2rB-j60l5Ltz3FiRCuOR4evFXtJHC34,909
+pygame/tests/run_tests__tests/incomplete_todo/fake_3_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/infinite_loop/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/infinite_loop/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_1_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/infinite_loop/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/infinite_loop/fake_1_test.py,sha256=rNt-VaNziz7OmfbDXcbXbDIbwC_6ScFJ-MtenMjR68Y,906
+pygame/tests/run_tests__tests/infinite_loop/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/print_stderr/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/print_stderr/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_3_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stderr/__pycache__/fake_4_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stderr/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/print_stderr/fake_3_test.py,sha256=6AGEff135DU_spRhZ09oDGXE4lZC3dlHU_phnfOyWYY,954
+pygame/tests/run_tests__tests/print_stderr/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949
+pygame/tests/run_tests__tests/print_stdout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/print_stdout/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_3_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stdout/__pycache__/fake_4_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/print_stdout/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/print_stdout/fake_3_test.py,sha256=cruYqrh3O3MQ8fczEFloLpsrQrYmMOd6jgxMU6e5H8w,1012
+pygame/tests/run_tests__tests/print_stdout/fake_4_test.py,sha256=xWpIVUpzevSs4bVeze48Q9jkZzss4szdw6eMOrJnZV8,949
+pygame/tests/run_tests__tests/run_tests__test.py,sha256=9mDlobUHX5baqNIJvCo4hN7XGb5qRIRO_DFfNcOno5I,4315
+pygame/tests/run_tests__tests/timeout/__init__.py,sha256=9_8wL9Scv8_Cs8HJyJHGvx1vwXErsuvlsAqNZLcJQR0,8
+pygame/tests/run_tests__tests/timeout/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/run_tests__tests/timeout/__pycache__/fake_2_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/timeout/__pycache__/sleep_test.cpython-36.pyc,,
+pygame/tests/run_tests__tests/timeout/fake_2_test.py,sha256=zFUNsDLmH9Pvpo-YEpvfW2raQyA6EL_BW3CuP10YIKU,899
+pygame/tests/run_tests__tests/timeout/sleep_test.py,sha256=5EDW4U6kYN4QIid0IgHBypJ3T3a78pILILF41DPpujk,716
+pygame/tests/rwobject_test.py,sha256=LAJun6obwHADEiONe6F68WNM9qAuL7i4hqbPErjmox4,4323
+pygame/tests/scrap_tags.py,sha256=zHyLWy2JRyfw0DamlH9dz-MZq2R2uOryjH9JRu-RCkw,671
+pygame/tests/scrap_test.py,sha256=qt47IQLTs3jqf8FEP1mBC8gX6SzbeLsjJnIdszMhbYU,9160
+pygame/tests/sndarray_tags.py,sha256=ThDQxqGFaAembuWgdYGsFSWEppVezgXJ2htYRvvDaXE,190
+pygame/tests/sndarray_test.py,sha256=BOv9SLpvUiQraLEykbOHKyE4jkSJuKYPd1IRUdU510Y,6290
+pygame/tests/sprite_test.py,sha256=08ZqxvMi7Bii4gGFVy2NdKJ3yWKz1an6AyEQs8Qhmqs,47210
+pygame/tests/surface_test.py,sha256=1bzv6cwWVL8wxNk0ZlX8iGVrP2Ep8NagqsP_iem8qKI,165585
+pygame/tests/surfarray_tags.py,sha256=AwlglKM7DrjHvvcSMm-yXb-PSxsVhJkS6VE7Z8wOhes,260
+pygame/tests/surfarray_test.py,sha256=mYy2tMofKo6ESr70UOc-qxxU2X22sUKw_zyMHmwHgFc,25812
+pygame/tests/surflock_test.py,sha256=dMZkzND7-R_z-GaxN4ZIcpNtW3PsK1i7kdnizpU21UY,4728
+pygame/tests/sysfont_test.py,sha256=uf7ISqCvoiegyUvYTqOT1-kYQi_POeejL1TqXXeYF9Q,1463
+pygame/tests/test_utils/__init__.py,sha256=_eQvMYQ_-gFuB1fiftJLjL0RYqV3tl5jRYeBUzI8x-4,4429
+pygame/tests/test_utils/__pycache__/__init__.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/arrinter.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/async_sub.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/buftools.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/endian.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/png.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/run_tests.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/test_machinery.cpython-36.pyc,,
+pygame/tests/test_utils/__pycache__/test_runner.cpython-36.pyc,,
+pygame/tests/test_utils/arrinter.py,sha256=xmfJDli6Q3SPBy5cLAF0SqTXlRc5QDZRhbDP50p5pjc,14698
+pygame/tests/test_utils/async_sub.py,sha256=2BqxoeKTo6sPlW2bwGCieEb0lwxZsHUHBnGFjSPny68,9128
+pygame/tests/test_utils/buftools.py,sha256=9bae7xJK78yZQk1Pc2juF-1Gp19Zpwsl3ujnCfsh48o,23641
+pygame/tests/test_utils/endian.py,sha256=Rc7rl38YamHgi8EzB92Muu8C4XH6yltH9f5On7qfMpY,495
+pygame/tests/test_utils/png.py,sha256=dxobMiiDvvAEZSNFoy0EkQxYoLNzfc4rHKLwYM58PEQ,152320
+pygame/tests/test_utils/run_tests.py,sha256=KeqrO4gUuPx6ElW9x-AuS_02Nqk7hMLasFFGfO-CjfY,12039
+pygame/tests/test_utils/test_machinery.py,sha256=4vsi3mOw_581298rsp4cmcQYpbB6P5PK3Eg0cTBy138,2429
+pygame/tests/test_utils/test_runner.py,sha256=ktlXMmx0LOD9gmeTXyk72Ozo1UkYlUUJGoYoohdMZfY,9328
+pygame/tests/threads_test.py,sha256=WWTg2G0x3eP0JxWZa3qGCbkRMWfiNCpkX9-_e0aUWZY,7833
+pygame/tests/time_test.py,sha256=lQTubG8CiDnTwHOcV0Esvk1QJX90vjbxZNDdorg1wZU,16065
+pygame/tests/touch_test.py,sha256=9e5LDHeZrtQSaUBrb9dLHT1YDuNuccSlYvcYIWYIm7k,3216
+pygame/tests/transform_test.py,sha256=EryZIaG25yunlIW9wS4pnQhtswVNI-IBp9oAJzTe7zY,53328
+pygame/tests/version_test.py,sha256=dvNIneFf1c4PAKa4xo1YLAlYRDFYL8_ZeUwI1FS55Is,1536
+pygame/tests/video_test.py,sha256=USdLAov8GnN_9vsXl_1pe0MI0ddM3KurURDhVQIV3nY,694
+pygame/threads/__init__.py,sha256=mkOLm--1G55m8Vj5AY30IMUhMfL1lBjZxcgNESooZBw,8063
+pygame/threads/__pycache__/__init__.cpython-36.pyc,,
+pygame/time.cp36-win_amd64.pyd,sha256=8XP7eGid-b52ppHXX6zxOrDIHEqbhLqX5cbwHlkpl14,18944
+pygame/time.pyi,sha256=0dUmVQjxwyhXkSRGX0DnqM2-0JzOMIVbHx4psLfrlhI,501
+pygame/transform.cp36-win_amd64.pyd,sha256=_aKj3g_SmRLx36TfqmNdUZXCe94VbexEBJZJF_9A68E,58368
+pygame/transform.pyi,sha256=EsjIBP706hur5c6aKmUBMbCp9cPQIilWcqkSHkCeUk4,1999
+pygame/version.py,sha256=fMY0j8dhul8aqGWskX4naIsnDXXx7l-jxex9FPRf6p0,2526
+pygame/version.pyi,sha256=NvmU4694WwSRWMiZ5WL3APQYA2s_3xY4foOnPsncNuM,600
+pygame/zlib1.dll,sha256=sfWKF_O_1VUj5772haz1sy0cKm8lq9zUQmgSZv0mqwg,108544
diff --git a/laplas/tools/abstract_map/flask/py.typed b/laplas/abstract_map/pygame-2.6.0.dist-info/REQUESTED
similarity index 100%
rename from laplas/tools/abstract_map/flask/py.typed
rename to laplas/abstract_map/pygame-2.6.0.dist-info/REQUESTED
diff --git a/laplas/abstract_map/pygame-2.6.0.dist-info/WHEEL b/laplas/abstract_map/pygame-2.6.0.dist-info/WHEEL
new file mode 100644
index 00000000..83ad8ebf
--- /dev/null
+++ b/laplas/abstract_map/pygame-2.6.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: bdist_wheel (0.37.1)
+Root-Is-Purelib: false
+Tag: cp36-cp36m-win_amd64
+
diff --git a/laplas/abstract_map/pygame-2.6.0.dist-info/entry_points.txt b/laplas/abstract_map/pygame-2.6.0.dist-info/entry_points.txt
new file mode 100644
index 00000000..e5a477eb
--- /dev/null
+++ b/laplas/abstract_map/pygame-2.6.0.dist-info/entry_points.txt
@@ -0,0 +1,3 @@
+[pyinstaller40]
+hook-dirs = pygame.__pyinstaller:get_hook_dirs
+
diff --git a/laplas/abstract_map/pygame-2.6.0.dist-info/top_level.txt b/laplas/abstract_map/pygame-2.6.0.dist-info/top_level.txt
new file mode 100644
index 00000000..0cb7ff1d
--- /dev/null
+++ b/laplas/abstract_map/pygame-2.6.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+pygame
diff --git a/laplas/tools/abstract_map/pygame/SDL2.dll b/laplas/abstract_map/pygame/SDL2.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/SDL2.dll
rename to laplas/abstract_map/pygame/SDL2.dll
diff --git a/laplas/tools/abstract_map/pygame/SDL2_image.dll b/laplas/abstract_map/pygame/SDL2_image.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/SDL2_image.dll
rename to laplas/abstract_map/pygame/SDL2_image.dll
diff --git a/laplas/tools/abstract_map/pygame/SDL2_mixer.dll b/laplas/abstract_map/pygame/SDL2_mixer.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/SDL2_mixer.dll
rename to laplas/abstract_map/pygame/SDL2_mixer.dll
diff --git a/laplas/tools/abstract_map/pygame/SDL2_ttf.dll b/laplas/abstract_map/pygame/SDL2_ttf.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/SDL2_ttf.dll
rename to laplas/abstract_map/pygame/SDL2_ttf.dll
diff --git a/laplas/tools/abstract_map/pygame/__init__.py b/laplas/abstract_map/pygame/__init__.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/__init__.py
rename to laplas/abstract_map/pygame/__init__.py
diff --git a/laplas/tools/abstract_map/pygame/__init__.pyi b/laplas/abstract_map/pygame/__init__.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/__init__.pyi
rename to laplas/abstract_map/pygame/__init__.pyi
diff --git a/laplas/tools/abstract_map/pygame/__pyinstaller/__init__.py b/laplas/abstract_map/pygame/__pyinstaller/__init__.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/__pyinstaller/__init__.py
rename to laplas/abstract_map/pygame/__pyinstaller/__init__.py
diff --git a/laplas/tools/abstract_map/pygame/__pyinstaller/hook-pygame.py b/laplas/abstract_map/pygame/__pyinstaller/hook-pygame.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/__pyinstaller/hook-pygame.py
rename to laplas/abstract_map/pygame/__pyinstaller/hook-pygame.py
diff --git a/laplas/tools/abstract_map/pygame/_camera_opencv.py b/laplas/abstract_map/pygame/_camera_opencv.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_camera_opencv.py
rename to laplas/abstract_map/pygame/_camera_opencv.py
diff --git a/laplas/tools/abstract_map/pygame/_camera_vidcapture.py b/laplas/abstract_map/pygame/_camera_vidcapture.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_camera_vidcapture.py
rename to laplas/abstract_map/pygame/_camera_vidcapture.py
diff --git a/laplas/tools/abstract_map/pygame/_common.pyi b/laplas/abstract_map/pygame/_common.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_common.pyi
rename to laplas/abstract_map/pygame/_common.pyi
diff --git a/laplas/tools/abstract_map/pygame/_sdl2/__init__.py b/laplas/abstract_map/pygame/_sdl2/__init__.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_sdl2/__init__.py
rename to laplas/abstract_map/pygame/_sdl2/__init__.py
diff --git a/laplas/tools/abstract_map/pygame/_sdl2/__init__.pyi b/laplas/abstract_map/pygame/_sdl2/__init__.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_sdl2/__init__.pyi
rename to laplas/abstract_map/pygame/_sdl2/__init__.pyi
diff --git a/laplas/tools/abstract_map/pygame/_sdl2/audio.pyi b/laplas/abstract_map/pygame/_sdl2/audio.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_sdl2/audio.pyi
rename to laplas/abstract_map/pygame/_sdl2/audio.pyi
diff --git a/laplas/tools/abstract_map/pygame/_sdl2/controller.pyi b/laplas/abstract_map/pygame/_sdl2/controller.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_sdl2/controller.pyi
rename to laplas/abstract_map/pygame/_sdl2/controller.pyi
diff --git a/laplas/tools/abstract_map/pygame/_sdl2/sdl2.pyi b/laplas/abstract_map/pygame/_sdl2/sdl2.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_sdl2/sdl2.pyi
rename to laplas/abstract_map/pygame/_sdl2/sdl2.pyi
diff --git a/laplas/tools/abstract_map/pygame/_sdl2/touch.pyi b/laplas/abstract_map/pygame/_sdl2/touch.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_sdl2/touch.pyi
rename to laplas/abstract_map/pygame/_sdl2/touch.pyi
diff --git a/laplas/tools/abstract_map/pygame/_sdl2/video.pyi b/laplas/abstract_map/pygame/_sdl2/video.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/_sdl2/video.pyi
rename to laplas/abstract_map/pygame/_sdl2/video.pyi
diff --git a/laplas/tools/abstract_map/pygame/base.pyi b/laplas/abstract_map/pygame/base.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/base.pyi
rename to laplas/abstract_map/pygame/base.pyi
diff --git a/laplas/tools/abstract_map/pygame/bufferproxy.pyi b/laplas/abstract_map/pygame/bufferproxy.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/bufferproxy.pyi
rename to laplas/abstract_map/pygame/bufferproxy.pyi
diff --git a/laplas/tools/abstract_map/pygame/camera.py b/laplas/abstract_map/pygame/camera.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/camera.py
rename to laplas/abstract_map/pygame/camera.py
diff --git a/laplas/tools/abstract_map/pygame/camera.pyi b/laplas/abstract_map/pygame/camera.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/camera.pyi
rename to laplas/abstract_map/pygame/camera.pyi
diff --git a/laplas/tools/abstract_map/pygame/color.pyi b/laplas/abstract_map/pygame/color.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/color.pyi
rename to laplas/abstract_map/pygame/color.pyi
diff --git a/laplas/tools/abstract_map/pygame/colordict.py b/laplas/abstract_map/pygame/colordict.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/colordict.py
rename to laplas/abstract_map/pygame/colordict.py
diff --git a/laplas/tools/abstract_map/pygame/constants.pyi b/laplas/abstract_map/pygame/constants.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/constants.pyi
rename to laplas/abstract_map/pygame/constants.pyi
diff --git a/laplas/tools/abstract_map/pygame/cursors.py b/laplas/abstract_map/pygame/cursors.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/cursors.py
rename to laplas/abstract_map/pygame/cursors.py
diff --git a/laplas/tools/abstract_map/pygame/cursors.pyi b/laplas/abstract_map/pygame/cursors.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/cursors.pyi
rename to laplas/abstract_map/pygame/cursors.pyi
diff --git a/laplas/tools/abstract_map/pygame/display.pyi b/laplas/abstract_map/pygame/display.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/display.pyi
rename to laplas/abstract_map/pygame/display.pyi
diff --git a/laplas/tools/abstract_map/pygame/draw.pyi b/laplas/abstract_map/pygame/draw.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/draw.pyi
rename to laplas/abstract_map/pygame/draw.pyi
diff --git a/laplas/tools/abstract_map/pygame/draw_py.py b/laplas/abstract_map/pygame/draw_py.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/draw_py.py
rename to laplas/abstract_map/pygame/draw_py.py
diff --git a/laplas/tools/abstract_map/pygame/event.pyi b/laplas/abstract_map/pygame/event.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/event.pyi
rename to laplas/abstract_map/pygame/event.pyi
diff --git a/laplas/tools/abstract_map/pygame/fastevent.py b/laplas/abstract_map/pygame/fastevent.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/fastevent.py
rename to laplas/abstract_map/pygame/fastevent.py
diff --git a/laplas/tools/abstract_map/pygame/fastevent.pyi b/laplas/abstract_map/pygame/fastevent.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/fastevent.pyi
rename to laplas/abstract_map/pygame/fastevent.pyi
diff --git a/laplas/tools/abstract_map/pygame/font.pyi b/laplas/abstract_map/pygame/font.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/font.pyi
rename to laplas/abstract_map/pygame/font.pyi
diff --git a/laplas/tools/abstract_map/pygame/freesansbold.ttf b/laplas/abstract_map/pygame/freesansbold.ttf
similarity index 100%
rename from laplas/tools/abstract_map/pygame/freesansbold.ttf
rename to laplas/abstract_map/pygame/freesansbold.ttf
diff --git a/laplas/tools/abstract_map/pygame/freetype.dll b/laplas/abstract_map/pygame/freetype.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/freetype.dll
rename to laplas/abstract_map/pygame/freetype.dll
diff --git a/laplas/tools/abstract_map/pygame/freetype.py b/laplas/abstract_map/pygame/freetype.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/freetype.py
rename to laplas/abstract_map/pygame/freetype.py
diff --git a/laplas/tools/abstract_map/pygame/freetype.pyi b/laplas/abstract_map/pygame/freetype.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/freetype.pyi
rename to laplas/abstract_map/pygame/freetype.pyi
diff --git a/laplas/tools/abstract_map/pygame/ftfont.py b/laplas/abstract_map/pygame/ftfont.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/ftfont.py
rename to laplas/abstract_map/pygame/ftfont.py
diff --git a/laplas/tools/abstract_map/pygame/gfxdraw.pyi b/laplas/abstract_map/pygame/gfxdraw.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/gfxdraw.pyi
rename to laplas/abstract_map/pygame/gfxdraw.pyi
diff --git a/laplas/tools/abstract_map/pygame/image.pyi b/laplas/abstract_map/pygame/image.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/image.pyi
rename to laplas/abstract_map/pygame/image.pyi
diff --git a/laplas/tools/abstract_map/pygame/joystick.pyi b/laplas/abstract_map/pygame/joystick.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/joystick.pyi
rename to laplas/abstract_map/pygame/joystick.pyi
diff --git a/laplas/tools/abstract_map/pygame/key.pyi b/laplas/abstract_map/pygame/key.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/key.pyi
rename to laplas/abstract_map/pygame/key.pyi
diff --git a/laplas/tools/abstract_map/pygame/libjpeg-9.dll b/laplas/abstract_map/pygame/libjpeg-9.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libjpeg-9.dll
rename to laplas/abstract_map/pygame/libjpeg-9.dll
diff --git a/laplas/tools/abstract_map/pygame/libmodplug-1.dll b/laplas/abstract_map/pygame/libmodplug-1.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libmodplug-1.dll
rename to laplas/abstract_map/pygame/libmodplug-1.dll
diff --git a/laplas/tools/abstract_map/pygame/libogg-0.dll b/laplas/abstract_map/pygame/libogg-0.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libogg-0.dll
rename to laplas/abstract_map/pygame/libogg-0.dll
diff --git a/laplas/tools/abstract_map/pygame/libopus-0.dll b/laplas/abstract_map/pygame/libopus-0.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libopus-0.dll
rename to laplas/abstract_map/pygame/libopus-0.dll
diff --git a/laplas/tools/abstract_map/pygame/libopusfile-0.dll b/laplas/abstract_map/pygame/libopusfile-0.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libopusfile-0.dll
rename to laplas/abstract_map/pygame/libopusfile-0.dll
diff --git a/laplas/tools/abstract_map/pygame/libpng16-16.dll b/laplas/abstract_map/pygame/libpng16-16.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libpng16-16.dll
rename to laplas/abstract_map/pygame/libpng16-16.dll
diff --git a/laplas/tools/abstract_map/pygame/libtiff-5.dll b/laplas/abstract_map/pygame/libtiff-5.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libtiff-5.dll
rename to laplas/abstract_map/pygame/libtiff-5.dll
diff --git a/laplas/tools/abstract_map/pygame/libwebp-7.dll b/laplas/abstract_map/pygame/libwebp-7.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/libwebp-7.dll
rename to laplas/abstract_map/pygame/libwebp-7.dll
diff --git a/laplas/tools/abstract_map/pygame/locals.py b/laplas/abstract_map/pygame/locals.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/locals.py
rename to laplas/abstract_map/pygame/locals.py
diff --git a/laplas/tools/abstract_map/pygame/locals.pyi b/laplas/abstract_map/pygame/locals.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/locals.pyi
rename to laplas/abstract_map/pygame/locals.pyi
diff --git a/laplas/tools/abstract_map/pygame/macosx.py b/laplas/abstract_map/pygame/macosx.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/macosx.py
rename to laplas/abstract_map/pygame/macosx.py
diff --git a/laplas/tools/abstract_map/pygame/mask.pyi b/laplas/abstract_map/pygame/mask.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/mask.pyi
rename to laplas/abstract_map/pygame/mask.pyi
diff --git a/laplas/tools/abstract_map/pygame/math.pyi b/laplas/abstract_map/pygame/math.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/math.pyi
rename to laplas/abstract_map/pygame/math.pyi
diff --git a/laplas/tools/abstract_map/pygame/midi.py b/laplas/abstract_map/pygame/midi.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/midi.py
rename to laplas/abstract_map/pygame/midi.py
diff --git a/laplas/tools/abstract_map/pygame/midi.pyi b/laplas/abstract_map/pygame/midi.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/midi.pyi
rename to laplas/abstract_map/pygame/midi.pyi
diff --git a/laplas/tools/abstract_map/pygame/mixer.pyi b/laplas/abstract_map/pygame/mixer.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/mixer.pyi
rename to laplas/abstract_map/pygame/mixer.pyi
diff --git a/laplas/tools/abstract_map/pygame/mixer_music.pyi b/laplas/abstract_map/pygame/mixer_music.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/mixer_music.pyi
rename to laplas/abstract_map/pygame/mixer_music.pyi
diff --git a/laplas/tools/abstract_map/pygame/mouse.pyi b/laplas/abstract_map/pygame/mouse.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/mouse.pyi
rename to laplas/abstract_map/pygame/mouse.pyi
diff --git a/laplas/tools/abstract_map/pygame/pixelarray.pyi b/laplas/abstract_map/pygame/pixelarray.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/pixelarray.pyi
rename to laplas/abstract_map/pygame/pixelarray.pyi
diff --git a/laplas/tools/abstract_map/pygame/pixelcopy.pyi b/laplas/abstract_map/pygame/pixelcopy.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/pixelcopy.pyi
rename to laplas/abstract_map/pygame/pixelcopy.pyi
diff --git a/laplas/tools/abstract_map/pygame/pkgdata.py b/laplas/abstract_map/pygame/pkgdata.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/pkgdata.py
rename to laplas/abstract_map/pygame/pkgdata.py
diff --git a/laplas/tools/abstract_map/pygame/portmidi.dll b/laplas/abstract_map/pygame/portmidi.dll
similarity index 100%
rename from laplas/tools/abstract_map/pygame/portmidi.dll
rename to laplas/abstract_map/pygame/portmidi.dll
diff --git a/laplas/tools/abstract_map/pygame/py.typed b/laplas/abstract_map/pygame/py.typed
similarity index 100%
rename from laplas/tools/abstract_map/pygame/py.typed
rename to laplas/abstract_map/pygame/py.typed
diff --git a/laplas/tools/abstract_map/pygame/pygame.ico b/laplas/abstract_map/pygame/pygame.ico
similarity index 100%
rename from laplas/tools/abstract_map/pygame/pygame.ico
rename to laplas/abstract_map/pygame/pygame.ico
diff --git a/laplas/tools/abstract_map/pygame/pygame_icon.bmp b/laplas/abstract_map/pygame/pygame_icon.bmp
similarity index 100%
rename from laplas/tools/abstract_map/pygame/pygame_icon.bmp
rename to laplas/abstract_map/pygame/pygame_icon.bmp
diff --git a/laplas/tools/abstract_map/pygame/pygame_icon.icns b/laplas/abstract_map/pygame/pygame_icon.icns
similarity index 100%
rename from laplas/tools/abstract_map/pygame/pygame_icon.icns
rename to laplas/abstract_map/pygame/pygame_icon.icns
diff --git a/laplas/tools/abstract_map/pygame/pygame_icon_mac.bmp b/laplas/abstract_map/pygame/pygame_icon_mac.bmp
similarity index 100%
rename from laplas/tools/abstract_map/pygame/pygame_icon_mac.bmp
rename to laplas/abstract_map/pygame/pygame_icon_mac.bmp
diff --git a/laplas/tools/abstract_map/pygame/rect.pyi b/laplas/abstract_map/pygame/rect.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/rect.pyi
rename to laplas/abstract_map/pygame/rect.pyi
diff --git a/laplas/tools/abstract_map/pygame/rwobject.pyi b/laplas/abstract_map/pygame/rwobject.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/rwobject.pyi
rename to laplas/abstract_map/pygame/rwobject.pyi
diff --git a/laplas/tools/abstract_map/pygame/scrap.pyi b/laplas/abstract_map/pygame/scrap.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/scrap.pyi
rename to laplas/abstract_map/pygame/scrap.pyi
diff --git a/laplas/tools/abstract_map/pygame/sndarray.py b/laplas/abstract_map/pygame/sndarray.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/sndarray.py
rename to laplas/abstract_map/pygame/sndarray.py
diff --git a/laplas/tools/abstract_map/pygame/sndarray.pyi b/laplas/abstract_map/pygame/sndarray.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/sndarray.pyi
rename to laplas/abstract_map/pygame/sndarray.pyi
diff --git a/laplas/tools/abstract_map/pygame/sprite.py b/laplas/abstract_map/pygame/sprite.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/sprite.py
rename to laplas/abstract_map/pygame/sprite.py
diff --git a/laplas/tools/abstract_map/pygame/sprite.pyi b/laplas/abstract_map/pygame/sprite.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/sprite.pyi
rename to laplas/abstract_map/pygame/sprite.pyi
diff --git a/laplas/tools/abstract_map/pygame/surface.pyi b/laplas/abstract_map/pygame/surface.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/surface.pyi
rename to laplas/abstract_map/pygame/surface.pyi
diff --git a/laplas/tools/abstract_map/pygame/surfarray.py b/laplas/abstract_map/pygame/surfarray.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/surfarray.py
rename to laplas/abstract_map/pygame/surfarray.py
diff --git a/laplas/tools/abstract_map/pygame/surfarray.pyi b/laplas/abstract_map/pygame/surfarray.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/surfarray.pyi
rename to laplas/abstract_map/pygame/surfarray.pyi
diff --git a/laplas/tools/abstract_map/pygame/surflock.pyi b/laplas/abstract_map/pygame/surflock.pyi
similarity index 100%
rename from laplas/tools/abstract_map/pygame/surflock.pyi
rename to laplas/abstract_map/pygame/surflock.pyi
diff --git a/laplas/tools/abstract_map/pygame/sysfont.py b/laplas/abstract_map/pygame/sysfont.py
similarity index 100%
rename from laplas/tools/abstract_map/pygame/sysfont.py
rename to laplas/abstract_map/pygame/sysfont.py
diff --git a/laplas/abstract_map/pygame/tests/__init__.py b/laplas/abstract_map/pygame/tests/__init__.py
new file mode 100644
index 00000000..dd265869
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/__init__.py
@@ -0,0 +1,40 @@
+"""Pygame unit test suite package
+
+Exports function run()
+
+A quick way to run the test suite package from the command line
+is by importing the go submodule:
+
+python -m "import pygame.tests" []
+
+Command line option --help displays a usage message. Available options
+correspond to the pygame.tests.run arguments.
+
+The xxxx_test submodules of the tests package are unit test suites for
+individual parts of Pygame. Each can also be run as a main program. This is
+useful if the test, such as cdrom_test, is interactive.
+
+For Pygame development the test suite can be run from a Pygame distribution
+root directory using run_tests.py. Alternately, test/__main__.py can be run
+directly.
+
+"""
+
+if __name__ == "pygame.tests":
+ from pygame.tests.test_utils.run_tests import run
+elif __name__ == "__main__":
+ import os
+ import sys
+
+ pkg_dir = os.path.split(os.path.abspath(__file__))[0]
+ parent_dir, pkg_name = os.path.split(pkg_dir)
+ is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame"
+ if not is_pygame_pkg:
+ sys.path.insert(0, parent_dir)
+
+ if is_pygame_pkg:
+ import pygame.tests.__main__
+ else:
+ import test.__main__
+else:
+ from test.test_utils.run_tests import run
diff --git a/laplas/abstract_map/pygame/tests/__main__.py b/laplas/abstract_map/pygame/tests/__main__.py
new file mode 100644
index 00000000..9b4cedde
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/__main__.py
@@ -0,0 +1,143 @@
+"""Load and run the Pygame test suite
+
+python -c "import pygame.tests.go" []
+
+or
+
+python test/go.py []
+
+Command line option --help displays a command line usage message.
+
+run_tests.py in the main distribution directory is an alternative to test.go
+
+"""
+
+import sys
+
+if __name__ == "__main__":
+ import os
+
+ pkg_dir = os.path.split(os.path.abspath(__file__))[0]
+ parent_dir, pkg_name = os.path.split(pkg_dir)
+ is_pygame_pkg = pkg_name == "tests" and os.path.split(parent_dir)[1] == "pygame"
+ if not is_pygame_pkg:
+ sys.path.insert(0, parent_dir)
+else:
+ is_pygame_pkg = __name__.startswith("pygame.tests.")
+
+if is_pygame_pkg:
+ from pygame.tests.test_utils.run_tests import run_and_exit
+ from pygame.tests.test_utils.test_runner import opt_parser
+else:
+ from test.test_utils.run_tests import run_and_exit
+ from test.test_utils.test_runner import opt_parser
+
+if is_pygame_pkg:
+ test_pkg_name = "pygame.tests"
+else:
+ test_pkg_name = "test"
+program_name = sys.argv[0]
+if program_name == "-c":
+ program_name = f'python -c "import {test_pkg_name}.go"'
+
+###########################################################################
+# Set additional command line options
+#
+# Defined in test_runner.py as it shares options, added to here
+
+opt_parser.set_usage(
+ f"""
+
+Runs all or some of the {test_pkg_name}.xxxx_test tests.
+
+$ {program_name} sprite threads -sd
+
+Runs the sprite and threads module tests isolated in subprocesses, dumping
+all failing tests info in the form of a dict.
+
+"""
+)
+
+opt_parser.add_option(
+ "-d", "--dump", action="store_true", help="dump results as dict ready to eval"
+)
+
+opt_parser.add_option("-F", "--file", help="dump results to a file")
+
+opt_parser.add_option(
+ "-m",
+ "--multi_thread",
+ metavar="THREADS",
+ type="int",
+ help="run subprocessed tests in x THREADS",
+)
+
+opt_parser.add_option(
+ "-t",
+ "--time_out",
+ metavar="SECONDS",
+ type="int",
+ help="kill stalled subprocessed tests after SECONDS",
+)
+
+opt_parser.add_option(
+ "-f", "--fake", metavar="DIR", help="run fake tests in run_tests__tests/$DIR"
+)
+
+opt_parser.add_option(
+ "-p",
+ "--python",
+ metavar="PYTHON",
+ help="path to python executable to run subproccesed tests\n"
+ "default (sys.executable): %s" % sys.executable,
+)
+
+opt_parser.add_option(
+ "-I",
+ "--interactive",
+ action="store_true",
+ help="include tests requiring user input",
+)
+
+opt_parser.add_option("-S", "--seed", type="int", help="Randomisation seed")
+
+###########################################################################
+# Set run() keyword arguments according to command line arguments.
+# args will be the test module list, passed as positional argumemts.
+
+options, args = opt_parser.parse_args()
+kwds = {}
+if options.incomplete:
+ kwds["incomplete"] = True
+if options.usesubprocess:
+ kwds["usesubprocess"] = True
+else:
+ kwds["usesubprocess"] = False
+if options.dump:
+ kwds["dump"] = True
+if options.file:
+ kwds["file"] = options.file
+if options.exclude:
+ kwds["exclude"] = options.exclude
+if options.unbuffered:
+ kwds["unbuffered"] = True
+if options.randomize:
+ kwds["randomize"] = True
+if options.seed is not None:
+ kwds["seed"] = options.seed
+if options.multi_thread is not None:
+ kwds["multi_thread"] = options.multi_thread
+if options.time_out is not None:
+ kwds["time_out"] = options.time_out
+if options.fake:
+ kwds["fake"] = options.fake
+if options.python:
+ kwds["python"] = options.python
+if options.interactive:
+ kwds["interactive"] = True
+kwds["verbosity"] = options.verbosity if options.verbosity is not None else 1
+
+
+###########################################################################
+# Run the test suite.
+run_and_exit(*args, **kwds)
diff --git a/laplas/abstract_map/pygame/tests/base_test.py b/laplas/abstract_map/pygame/tests/base_test.py
new file mode 100644
index 00000000..b11d2d68
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/base_test.py
@@ -0,0 +1,623 @@
+import sys
+import unittest
+
+import platform
+
+IS_PYPY = "PyPy" == platform.python_implementation()
+
+try:
+ from pygame.tests.test_utils import arrinter
+except NameError:
+ pass
+import pygame
+
+
+quit_count = 0
+
+
+def quit_hook():
+ global quit_count
+ quit_count += 1
+
+
+class BaseModuleTest(unittest.TestCase):
+ def tearDown(self):
+ # Clean up after each test method.
+ pygame.quit()
+
+ def test_get_sdl_byteorder(self):
+ """Ensure the SDL byte order is valid"""
+ byte_order = pygame.get_sdl_byteorder()
+ expected_options = (pygame.LIL_ENDIAN, pygame.BIG_ENDIAN)
+
+ self.assertIn(byte_order, expected_options)
+
+ def test_get_sdl_version(self):
+ """Ensure the SDL version is valid"""
+ self.assertEqual(len(pygame.get_sdl_version()), 3)
+
+ class ExporterBase:
+ def __init__(self, shape, typechar, itemsize):
+ import ctypes
+
+ ndim = len(shape)
+ self.ndim = ndim
+ self.shape = tuple(shape)
+ array_len = 1
+ for d in shape:
+ array_len *= d
+ self.size = itemsize * array_len
+ self.parent = ctypes.create_string_buffer(self.size)
+ self.itemsize = itemsize
+ strides = [itemsize] * ndim
+ for i in range(ndim - 1, 0, -1):
+ strides[i - 1] = strides[i] * shape[i]
+ self.strides = tuple(strides)
+ self.data = ctypes.addressof(self.parent), False
+ if self.itemsize == 1:
+ byteorder = "|"
+ elif sys.byteorder == "big":
+ byteorder = ">"
+ else:
+ byteorder = "<"
+ self.typestr = byteorder + typechar + str(self.itemsize)
+
+ def assertSame(self, proxy, obj):
+ self.assertEqual(proxy.length, obj.size)
+ iface = proxy.__array_interface__
+ self.assertEqual(iface["typestr"], obj.typestr)
+ self.assertEqual(iface["shape"], obj.shape)
+ self.assertEqual(iface["strides"], obj.strides)
+ self.assertEqual(iface["data"], obj.data)
+
+ def test_PgObject_GetBuffer_array_interface(self):
+ from pygame.bufferproxy import BufferProxy
+
+ class Exporter(self.ExporterBase):
+ def get__array_interface__(self):
+ return {
+ "version": 3,
+ "typestr": self.typestr,
+ "shape": self.shape,
+ "strides": self.strides,
+ "data": self.data,
+ }
+
+ __array_interface__ = property(get__array_interface__)
+ # Should be ignored by PgObject_GetBuffer
+ __array_struct__ = property(lambda self: None)
+
+ _shape = [2, 3, 5, 7, 11] # Some prime numbers
+ for ndim in range(1, len(_shape)):
+ o = Exporter(_shape[0:ndim], "i", 2)
+ v = BufferProxy(o)
+ self.assertSame(v, o)
+ ndim = 2
+ shape = _shape[0:ndim]
+ for typechar in ("i", "u"):
+ for itemsize in (1, 2, 4, 8):
+ o = Exporter(shape, typechar, itemsize)
+ v = BufferProxy(o)
+ self.assertSame(v, o)
+ for itemsize in (4, 8):
+ o = Exporter(shape, "f", itemsize)
+ v = BufferProxy(o)
+ self.assertSame(v, o)
+
+ # Is the dict received from an exporting object properly released?
+ # The dict should be freed before PgObject_GetBuffer returns.
+ # When the BufferProxy v's length property is referenced, v calls
+ # PgObject_GetBuffer, which in turn references Exporter2 o's
+ # __array_interface__ property. The Exporter2 instance o returns a
+ # dict subclass for which it keeps both a regular reference and a
+ # weak reference. The regular reference should be the only
+ # remaining reference when PgObject_GetBuffer returns. This is
+ # verified by first checking the weak reference both before and
+ # after the regular reference held by o is removed.
+
+ import weakref, gc
+
+ class NoDictError(RuntimeError):
+ pass
+
+ class WRDict(dict):
+ """Weak referenceable dict"""
+
+ pass
+
+ class Exporter2(Exporter):
+ def get__array_interface__2(self):
+ self.d = WRDict(Exporter.get__array_interface__(self))
+ self.dict_ref = weakref.ref(self.d)
+ return self.d
+
+ __array_interface__ = property(get__array_interface__2)
+
+ def free_dict(self):
+ self.d = None
+
+ def is_dict_alive(self):
+ try:
+ return self.dict_ref() is not None
+ except AttributeError:
+ raise NoDictError("__array_interface__ is unread")
+
+ o = Exporter2((2, 4), "u", 4)
+ v = BufferProxy(o)
+ self.assertRaises(NoDictError, o.is_dict_alive)
+ length = v.length
+ self.assertTrue(o.is_dict_alive())
+ o.free_dict()
+ gc.collect()
+ self.assertFalse(o.is_dict_alive())
+
+ def test_GetView_array_struct(self):
+ from pygame.bufferproxy import BufferProxy
+
+ class Exporter(self.ExporterBase):
+ def __init__(self, shape, typechar, itemsize):
+ super().__init__(shape, typechar, itemsize)
+ self.view = BufferProxy(self.__dict__)
+
+ def get__array_struct__(self):
+ return self.view.__array_struct__
+
+ __array_struct__ = property(get__array_struct__)
+ # Should not cause PgObject_GetBuffer to fail
+ __array_interface__ = property(lambda self: None)
+
+ _shape = [2, 3, 5, 7, 11] # Some prime numbers
+ for ndim in range(1, len(_shape)):
+ o = Exporter(_shape[0:ndim], "i", 2)
+ v = BufferProxy(o)
+ self.assertSame(v, o)
+ ndim = 2
+ shape = _shape[0:ndim]
+ for typechar in ("i", "u"):
+ for itemsize in (1, 2, 4, 8):
+ o = Exporter(shape, typechar, itemsize)
+ v = BufferProxy(o)
+ self.assertSame(v, o)
+ for itemsize in (4, 8):
+ o = Exporter(shape, "f", itemsize)
+ v = BufferProxy(o)
+ self.assertSame(v, o)
+
+ # Check returned cobject/capsule reference count
+ try:
+ from sys import getrefcount
+ except ImportError:
+ # PyPy: no reference counting
+ pass
+ else:
+ o = Exporter(shape, typechar, itemsize)
+ self.assertEqual(getrefcount(o.__array_struct__), 1)
+
+ if pygame.HAVE_NEWBUF:
+ from pygame.tests.test_utils import buftools
+
+ def NEWBUF_assertSame(self, proxy, exp):
+ buftools = self.buftools
+ Importer = buftools.Importer
+ self.assertEqual(proxy.length, exp.len)
+ imp = Importer(proxy, buftools.PyBUF_RECORDS_RO)
+ self.assertEqual(imp.readonly, exp.readonly)
+ self.assertEqual(imp.format, exp.format)
+ self.assertEqual(imp.itemsize, exp.itemsize)
+ self.assertEqual(imp.ndim, exp.ndim)
+ self.assertEqual(imp.shape, exp.shape)
+ self.assertEqual(imp.strides, exp.strides)
+ self.assertTrue(imp.suboffsets is None)
+
+ @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
+ @unittest.skipIf(IS_PYPY, "pypy no likey")
+ def test_newbuf(self):
+ from pygame.bufferproxy import BufferProxy
+
+ Exporter = self.buftools.Exporter
+ _shape = [2, 3, 5, 7, 11] # Some prime numbers
+ for ndim in range(1, len(_shape)):
+ o = Exporter(_shape[0:ndim], "=h")
+ v = BufferProxy(o)
+ self.NEWBUF_assertSame(v, o)
+ ndim = 2
+ shape = _shape[0:ndim]
+ for format in [
+ "b",
+ "B",
+ "=h",
+ "=H",
+ "=i",
+ "=I",
+ "=q",
+ "=Q",
+ "f",
+ "d",
+ "1h",
+ "=1h",
+ "x",
+ "1x",
+ "2x",
+ "3x",
+ "4x",
+ "5x",
+ "6x",
+ "7x",
+ "8x",
+ "9x",
+ ]:
+ o = Exporter(shape, format)
+ v = BufferProxy(o)
+ self.NEWBUF_assertSame(v, o)
+
+ @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
+ def test_bad_format(self):
+ from pygame.bufferproxy import BufferProxy
+ from pygame.newbuffer import BufferMixin
+ from ctypes import create_string_buffer, addressof
+
+ buftools = self.buftools
+ Exporter = buftools.Exporter
+ Importer = buftools.Importer
+ PyBUF_FORMAT = buftools.PyBUF_FORMAT
+
+ for format in [
+ "",
+ "=",
+ "1",
+ " ",
+ "2h",
+ "=2h",
+ "0x",
+ "11x",
+ "=!",
+ "h ",
+ " h",
+ "hh",
+ "?",
+ ]:
+ exp = Exporter((1,), format, itemsize=2)
+ b = BufferProxy(exp)
+ self.assertRaises(ValueError, Importer, b, PyBUF_FORMAT)
+
+ @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
+ @unittest.skipIf(IS_PYPY, "fails on pypy")
+ def test_PgDict_AsBuffer_PyBUF_flags(self):
+ from pygame.bufferproxy import BufferProxy
+
+ is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN
+ fsys, frev = ("<", ">") if is_lil_endian else (">", "<")
+ buftools = self.buftools
+ Importer = buftools.Importer
+ a = BufferProxy(
+ {"typestr": "|u4", "shape": (10, 2), "data": (9, False)}
+ ) # 9? No data accesses.
+ b = Importer(a, buftools.PyBUF_SIMPLE)
+ self.assertEqual(b.ndim, 0)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, a.length)
+ self.assertEqual(b.itemsize, 4)
+ self.assertTrue(b.shape is None)
+ self.assertTrue(b.strides is None)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, 9)
+ b = Importer(a, buftools.PyBUF_WRITABLE)
+ self.assertEqual(b.ndim, 0)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, a.length)
+ self.assertEqual(b.itemsize, 4)
+ self.assertTrue(b.shape is None)
+ self.assertTrue(b.strides is None)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, 9)
+ b = Importer(a, buftools.PyBUF_ND)
+ self.assertEqual(b.ndim, 2)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, a.length)
+ self.assertEqual(b.itemsize, 4)
+ self.assertEqual(b.shape, (10, 2))
+ self.assertTrue(b.strides is None)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, 9)
+ a = BufferProxy(
+ {
+ "typestr": fsys + "i2",
+ "shape": (5, 10),
+ "strides": (24, 2),
+ "data": (42, False),
+ }
+ ) # 42? No data accesses.
+ b = Importer(a, buftools.PyBUF_STRIDES)
+ self.assertEqual(b.ndim, 2)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, 100)
+ self.assertEqual(b.itemsize, 2)
+ self.assertEqual(b.shape, (5, 10))
+ self.assertEqual(b.strides, (24, 2))
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, 42)
+ b = Importer(a, buftools.PyBUF_FULL_RO)
+ self.assertEqual(b.ndim, 2)
+ self.assertEqual(b.format, "=h")
+ self.assertEqual(b.len, 100)
+ self.assertEqual(b.itemsize, 2)
+ self.assertEqual(b.shape, (5, 10))
+ self.assertEqual(b.strides, (24, 2))
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, 42)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG)
+ a = BufferProxy(
+ {
+ "typestr": frev + "i2",
+ "shape": (3, 5, 10),
+ "strides": (120, 24, 2),
+ "data": (1000000, True),
+ }
+ ) # 1000000? No data accesses.
+ b = Importer(a, buftools.PyBUF_FULL_RO)
+ self.assertEqual(b.ndim, 3)
+ self.assertEqual(b.format, frev + "h")
+ self.assertEqual(b.len, 300)
+ self.assertEqual(b.itemsize, 2)
+ self.assertEqual(b.shape, (3, 5, 10))
+ self.assertEqual(b.strides, (120, 24, 2))
+ self.assertTrue(b.suboffsets is None)
+ self.assertTrue(b.readonly)
+ self.assertEqual(b.buf, 1000000)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FULL)
+
+ @unittest.skipIf(IS_PYPY or (not pygame.HAVE_NEWBUF), "newbuf with ctypes")
+ def test_PgObject_AsBuffer_PyBUF_flags(self):
+ from pygame.bufferproxy import BufferProxy
+ import ctypes
+
+ is_lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN
+ fsys, frev = ("<", ">") if is_lil_endian else (">", "<")
+ buftools = self.buftools
+ Importer = buftools.Importer
+ e = arrinter.Exporter(
+ (10, 2), typekind="f", itemsize=ctypes.sizeof(ctypes.c_double)
+ )
+ a = BufferProxy(e)
+ b = Importer(a, buftools.PyBUF_SIMPLE)
+ self.assertEqual(b.ndim, 0)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, e.len)
+ self.assertEqual(b.itemsize, e.itemsize)
+ self.assertTrue(b.shape is None)
+ self.assertTrue(b.strides is None)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, e.data)
+ b = Importer(a, buftools.PyBUF_WRITABLE)
+ self.assertEqual(b.ndim, 0)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, e.len)
+ self.assertEqual(b.itemsize, e.itemsize)
+ self.assertTrue(b.shape is None)
+ self.assertTrue(b.strides is None)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, e.data)
+ b = Importer(a, buftools.PyBUF_ND)
+ self.assertEqual(b.ndim, e.nd)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, a.length)
+ self.assertEqual(b.itemsize, e.itemsize)
+ self.assertEqual(b.shape, e.shape)
+ self.assertTrue(b.strides is None)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, e.data)
+ e = arrinter.Exporter((5, 10), typekind="i", itemsize=2, strides=(24, 2))
+ a = BufferProxy(e)
+ b = Importer(a, buftools.PyBUF_STRIDES)
+ self.assertEqual(b.ndim, e.nd)
+ self.assertTrue(b.format is None)
+ self.assertEqual(b.len, e.len)
+ self.assertEqual(b.itemsize, e.itemsize)
+ self.assertEqual(b.shape, e.shape)
+ self.assertEqual(b.strides, e.strides)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, e.data)
+ b = Importer(a, buftools.PyBUF_FULL_RO)
+ self.assertEqual(b.ndim, e.nd)
+ self.assertEqual(b.format, "=h")
+ self.assertEqual(b.len, e.len)
+ self.assertEqual(b.itemsize, e.itemsize)
+ self.assertEqual(b.shape, e.shape)
+ self.assertEqual(b.strides, e.strides)
+ self.assertTrue(b.suboffsets is None)
+ self.assertFalse(b.readonly)
+ self.assertEqual(b.buf, e.data)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_WRITABLE)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_SIMPLE)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ND)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_C_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_F_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_ANY_CONTIGUOUS)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_CONTIG)
+ e = arrinter.Exporter(
+ (3, 5, 10),
+ typekind="i",
+ itemsize=2,
+ strides=(120, 24, 2),
+ flags=arrinter.PAI_ALIGNED,
+ )
+ a = BufferProxy(e)
+ b = Importer(a, buftools.PyBUF_FULL_RO)
+ self.assertEqual(b.ndim, e.nd)
+ self.assertEqual(b.format, frev + "h")
+ self.assertEqual(b.len, e.len)
+ self.assertEqual(b.itemsize, e.itemsize)
+ self.assertEqual(b.shape, e.shape)
+ self.assertEqual(b.strides, e.strides)
+ self.assertTrue(b.suboffsets is None)
+ self.assertTrue(b.readonly)
+ self.assertEqual(b.buf, e.data)
+ self.assertRaises(BufferError, Importer, a, buftools.PyBUF_FULL)
+
+ def test_PgObject_GetBuffer_exception(self):
+ # For consistency with surfarray
+ from pygame.bufferproxy import BufferProxy
+
+ bp = BufferProxy(1)
+ self.assertRaises(ValueError, getattr, bp, "length")
+
+ def not_init_assertions(self):
+ self.assertFalse(pygame.get_init(), "pygame shouldn't be initialized")
+ self.assertFalse(pygame.display.get_init(), "display shouldn't be initialized")
+
+ if "pygame.mixer" in sys.modules:
+ self.assertFalse(pygame.mixer.get_init(), "mixer shouldn't be initialized")
+
+ if "pygame.font" in sys.modules:
+ self.assertFalse(pygame.font.get_init(), "init shouldn't be initialized")
+
+ ## !!! TODO : Remove when scrap works for OS X
+ import platform
+
+ if platform.system().startswith("Darwin"):
+ return
+
+ try:
+ self.assertRaises(pygame.error, pygame.scrap.get)
+ except NotImplementedError:
+ # Scrap is optional.
+ pass
+
+ # pygame.cdrom
+ # pygame.joystick
+
+ def init_assertions(self):
+ self.assertTrue(pygame.get_init())
+ self.assertTrue(pygame.display.get_init())
+
+ if "pygame.mixer" in sys.modules:
+ self.assertTrue(pygame.mixer.get_init())
+
+ if "pygame.font" in sys.modules:
+ self.assertTrue(pygame.font.get_init())
+
+ def test_quit__and_init(self):
+ # __doc__ (as of 2008-06-25) for pygame.base.quit:
+
+ # pygame.quit(): return None
+ # uninitialize all pygame modules
+
+ # Make sure everything is not init
+ self.not_init_assertions()
+
+ # Initiate it
+ pygame.init()
+
+ # Check
+ self.init_assertions()
+
+ # Quit
+ pygame.quit()
+
+ # All modules have quit
+ self.not_init_assertions()
+
+ def test_register_quit(self):
+ """Ensure that a registered function is called on quit()"""
+ self.assertEqual(quit_count, 0)
+
+ pygame.init()
+ pygame.register_quit(quit_hook)
+ pygame.quit()
+
+ self.assertEqual(quit_count, 1)
+
+ def test_get_error(self):
+ # __doc__ (as of 2008-08-02) for pygame.base.get_error:
+
+ # pygame.get_error(): return errorstr
+ # get the current error message
+ #
+ # SDL maintains an internal error message. This message will usually
+ # be given to you when pygame.error is raised. You will rarely need to
+ # call this function.
+ #
+
+ # The first error could be all sorts of nonsense or empty.
+ e = pygame.get_error()
+ pygame.set_error("hi")
+ self.assertEqual(pygame.get_error(), "hi")
+ pygame.set_error("")
+ self.assertEqual(pygame.get_error(), "")
+
+ def test_set_error(self):
+ # The first error could be all sorts of nonsense or empty.
+ e = pygame.get_error()
+ pygame.set_error("hi")
+ self.assertEqual(pygame.get_error(), "hi")
+ pygame.set_error("")
+ self.assertEqual(pygame.get_error(), "")
+
+ def test_unicode_error(self):
+ pygame.set_error("你好")
+ self.assertEqual("你好", pygame.get_error())
+
+ def test_init(self):
+ """Ensures init() works properly."""
+ # Make sure nothing initialized.
+ self.not_init_assertions()
+
+ # display and joystick must init, at minimum
+ expected_min_passes = 2
+
+ # All modules should pass.
+ expected_fails = 0
+
+ passes, fails = pygame.init()
+
+ self.init_assertions()
+ self.assertGreaterEqual(passes, expected_min_passes)
+ self.assertEqual(fails, expected_fails)
+
+ def test_get_init(self):
+ # Test if get_init() gets the init state.
+ self.assertFalse(pygame.get_init())
+
+ def test_get_init__after_init(self):
+ # Test if get_init() gets the init state after pygame.init() called.
+ pygame.init()
+
+ self.assertTrue(pygame.get_init())
+
+ def test_get_init__after_quit(self):
+ # Test if get_init() gets the init state after pygame.quit() called.
+ pygame.init()
+ pygame.quit()
+
+ self.assertFalse(pygame.get_init())
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/blit_test.py b/laplas/abstract_map/pygame/tests/blit_test.py
new file mode 100644
index 00000000..dcd7d4ce
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/blit_test.py
@@ -0,0 +1,192 @@
+import unittest
+
+import pygame
+from pygame.locals import *
+
+from time import time
+
+
+class BlitTest(unittest.TestCase):
+ def test_SRCALPHA(self):
+ """SRCALPHA tests."""
+ # blend(s, 0, d) = d
+ s = pygame.Surface((1, 1), SRCALPHA, 32)
+ s.fill((255, 255, 255, 0))
+
+ d = pygame.Surface((1, 1), SRCALPHA, 32)
+ d.fill((0, 0, 255, 255))
+
+ s.blit(d, (0, 0))
+ self.assertEqual(s.get_at((0, 0)), d.get_at((0, 0)))
+
+ # blend(s, 255, d) = s
+ s = pygame.Surface((1, 1), SRCALPHA, 32)
+ s.fill((123, 0, 0, 255))
+ s1 = pygame.Surface((1, 1), SRCALPHA, 32)
+ s1.fill((123, 0, 0, 255))
+ d = pygame.Surface((1, 1), SRCALPHA, 32)
+ d.fill((10, 0, 0, 0))
+ s.blit(d, (0, 0))
+ self.assertEqual(s.get_at((0, 0)), s1.get_at((0, 0)))
+
+ # TODO: these should be true too.
+ # blend(0, sA, 0) = 0
+ # blend(255, sA, 255) = 255
+ # blend(s, sA, d) <= 255
+
+ def test_BLEND(self):
+ """BLEND_ tests."""
+
+ # test that it doesn't overflow, and that it is saturated.
+ s = pygame.Surface((1, 1), SRCALPHA, 32)
+ s.fill((255, 255, 255, 0))
+
+ d = pygame.Surface((1, 1), SRCALPHA, 32)
+ d.fill((0, 0, 255, 255))
+
+ s.blit(d, (0, 0), None, BLEND_ADD)
+
+ # print("d %s" % (d.get_at((0,0)),))
+ # print(s.get_at((0,0)))
+ # self.assertEqual(s.get_at((0,0))[2], 255 )
+ # self.assertEqual(s.get_at((0,0))[3], 0 )
+
+ s.blit(d, (0, 0), None, BLEND_RGBA_ADD)
+ # print(s.get_at((0,0)))
+ self.assertEqual(s.get_at((0, 0))[3], 255)
+
+ # test adding works.
+ s.fill((20, 255, 255, 0))
+ d.fill((10, 0, 255, 255))
+ s.blit(d, (0, 0), None, BLEND_ADD)
+ self.assertEqual(s.get_at((0, 0))[2], 255)
+
+ # test subbing works.
+ s.fill((20, 255, 255, 0))
+ d.fill((10, 0, 255, 255))
+ s.blit(d, (0, 0), None, BLEND_SUB)
+ self.assertEqual(s.get_at((0, 0))[0], 10)
+
+ # no overflow in sub blend.
+ s.fill((20, 255, 255, 0))
+ d.fill((30, 0, 255, 255))
+ s.blit(d, (0, 0), None, BLEND_SUB)
+ self.assertEqual(s.get_at((0, 0))[0], 0)
+
+
+class BlitsTest(unittest.TestCase):
+ """Tests for pygame.Surface.blits"""
+
+ def setUp(self):
+ self.NUM_SURFS = 255
+ self.PRINT_TIMING = 0
+ self.dst = pygame.Surface((self.NUM_SURFS * 10, 10), SRCALPHA, 32)
+ self.dst.fill((230, 230, 230))
+ self.blit_list = self.make_blit_list(self.NUM_SURFS)
+
+ def make_blit_list(self, num_surfs):
+ """Generate a list of tuples representing surfaces and destinations
+ for blitting"""
+
+ blit_list = []
+ for i in range(num_surfs):
+ dest = (i * 10, 0)
+ surf = pygame.Surface((10, 10), SRCALPHA, 32)
+ color = (i * 1, i * 1, i * 1)
+ surf.fill(color)
+ blit_list.append((surf, dest))
+ return blit_list
+
+ def custom_blits(self, blit_list):
+ """Custom blits method that manually iterates over the blit_list and blits
+ each surface onto the destination."""
+
+ for surface, dest in blit_list:
+ self.dst.blit(surface, dest)
+
+ def test_custom_blits_performance(self):
+ """Checks time performance of the custom blits method"""
+
+ t0 = time()
+ results = self.custom_blits(self.blit_list)
+ t1 = time()
+ if self.PRINT_TIMING:
+ print(f"python blits: {t1 - t0}")
+
+ def test_blits_performance(self):
+ """Checks time performance of blits"""
+
+ t0 = time()
+ results = self.dst.blits(self.blit_list)
+ t1 = time()
+ if self.PRINT_TIMING:
+ print(f"Surface.blits: {t1 - t0}")
+
+ # Measure time performance of blits with doreturn=0
+ t0 = time()
+ results = self.dst.blits(self.blit_list, doreturn=0)
+ t1 = time()
+ if self.PRINT_TIMING:
+ print(f"Surface.blits doreturn=0: {t1 - t0}")
+
+ # Measure time performance of blits using a generator
+ t0 = time()
+ results = self.dst.blits(((surf, dest) for surf, dest in self.blit_list))
+ t1 = time()
+ if self.PRINT_TIMING:
+ print(f"Surface.blits generator: {t1 - t0}")
+
+ def test_blits_correctness(self):
+ """Checks the correctness of the colors on the destination
+ after blitting and tests that the length of the results list
+ matches the number of surfaces blitted."""
+
+ results = self.dst.blits(self.blit_list)
+ for i in range(self.NUM_SURFS):
+ color = (i * 1, i * 1, i * 1)
+ self.assertEqual(self.dst.get_at((i * 10, 0)), color)
+ self.assertEqual(self.dst.get_at(((i * 10) + 5, 5)), color)
+
+ self.assertEqual(len(results), self.NUM_SURFS)
+
+ def test_blits_doreturn(self):
+ """Tests that when doreturn=0, it returns None"""
+
+ results = self.dst.blits(self.blit_list, doreturn=0)
+ self.assertEqual(results, None)
+
+ def test_blits_not_sequence(self):
+ """Tests that calling blits with an invalid non-sequence None argument
+ raises a ValueError."""
+
+ dst = pygame.Surface((100, 10), SRCALPHA, 32)
+ with self.assertRaises(ValueError):
+ dst.blits(None)
+
+ def test_blits_wrong_length(self):
+ """Tests that calling blits with an invalid sequence containing a single surface
+ (without a destination) raises a ValueError."""
+
+ dst = pygame.Surface((100, 10), SRCALPHA, 32)
+ with self.assertRaises(ValueError):
+ dst.blits([pygame.Surface((10, 10), SRCALPHA, 32)])
+
+ def test_blits_bad_surf_args(self):
+ """Tests that calling blits with a sequence containing an invalid tuple of
+ None arguments raises a TypeError."""
+
+ dst = pygame.Surface((100, 10), SRCALPHA, 32)
+ with self.assertRaises(TypeError):
+ dst.blits([(None, None)])
+
+ def test_blits_bad_dest(self):
+ """Tests that calling blits with a sequence containing an invalid tuple with a
+ destination of None raises a TypeError."""
+
+ dst = pygame.Surface((100, 10), SRCALPHA, 32)
+ with self.assertRaises(TypeError):
+ dst.blits([(pygame.Surface((10, 10), SRCALPHA, 32), None)])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/bufferproxy_test.py b/laplas/abstract_map/pygame/tests/bufferproxy_test.py
new file mode 100644
index 00000000..1282e35c
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/bufferproxy_test.py
@@ -0,0 +1,504 @@
+import re
+import weakref
+import gc
+import ctypes
+import unittest
+
+import pygame
+from pygame.bufferproxy import BufferProxy
+
+
+try:
+ BufferError
+except NameError:
+ from pygame import BufferError
+
+
+class BufferProxyTest(unittest.TestCase):
+ view_keywords = {
+ "shape": (5, 4, 3),
+ "typestr": "|u1",
+ "data": (0, True),
+ "strides": (4, 20, 1),
+ }
+
+ def test_module_name(self):
+ self.assertEqual(pygame.bufferproxy.__name__, "pygame.bufferproxy")
+
+ def test_class_name(self):
+ self.assertEqual(BufferProxy.__name__, "BufferProxy")
+
+ def test___array_struct___property(self):
+ kwds = self.view_keywords
+ v = BufferProxy(kwds)
+ d = pygame.get_array_interface(v)
+ self.assertEqual(len(d), 5)
+ self.assertEqual(d["version"], 3)
+ self.assertEqual(d["shape"], kwds["shape"])
+ self.assertEqual(d["typestr"], kwds["typestr"])
+ self.assertEqual(d["data"], kwds["data"])
+ self.assertEqual(d["strides"], kwds["strides"])
+
+ def test___array_interface___property(self):
+ kwds = self.view_keywords
+ v = BufferProxy(kwds)
+ d = v.__array_interface__
+ self.assertEqual(len(d), 5)
+ self.assertEqual(d["version"], 3)
+ self.assertEqual(d["shape"], kwds["shape"])
+ self.assertEqual(d["typestr"], kwds["typestr"])
+ self.assertEqual(d["data"], kwds["data"])
+ self.assertEqual(d["strides"], kwds["strides"])
+
+ def test_parent_property(self):
+ kwds = dict(self.view_keywords)
+ p = []
+ kwds["parent"] = p
+ v = BufferProxy(kwds)
+
+ self.assertIs(v.parent, p)
+
+ def test_before(self):
+ def callback(parent):
+ success.append(parent is p)
+
+ class MyException(Exception):
+ pass
+
+ def raise_exception(parent):
+ raise MyException("Just a test.")
+
+ kwds = dict(self.view_keywords)
+ p = []
+ kwds["parent"] = p
+
+ # For array interface
+ success = []
+ kwds["before"] = callback
+ v = BufferProxy(kwds)
+ self.assertEqual(len(success), 0)
+ d = v.__array_interface__
+ self.assertEqual(len(success), 1)
+ self.assertTrue(success[0])
+ d = v.__array_interface__
+ self.assertEqual(len(success), 1)
+ d = v = None
+ gc.collect()
+ self.assertEqual(len(success), 1)
+
+ # For array struct
+ success = []
+ kwds["before"] = callback
+ v = BufferProxy(kwds)
+ self.assertEqual(len(success), 0)
+ c = v.__array_struct__
+ self.assertEqual(len(success), 1)
+ self.assertTrue(success[0])
+ c = v.__array_struct__
+ self.assertEqual(len(success), 1)
+ c = v = None
+ gc.collect()
+ self.assertEqual(len(success), 1)
+
+ # Callback raises an exception
+ kwds["before"] = raise_exception
+ v = BufferProxy(kwds)
+ self.assertRaises(MyException, lambda: v.__array_struct__)
+
+ def test_after(self):
+ def callback(parent):
+ success.append(parent is p)
+
+ kwds = dict(self.view_keywords)
+ p = []
+ kwds["parent"] = p
+
+ # For array interface
+ success = []
+ kwds["after"] = callback
+ v = BufferProxy(kwds)
+ self.assertEqual(len(success), 0)
+ d = v.__array_interface__
+ self.assertEqual(len(success), 0)
+ d = v.__array_interface__
+ self.assertEqual(len(success), 0)
+ d = v = None
+ gc.collect()
+ self.assertEqual(len(success), 1)
+ self.assertTrue(success[0])
+
+ # For array struct
+ success = []
+ kwds["after"] = callback
+ v = BufferProxy(kwds)
+ self.assertEqual(len(success), 0)
+ c = v.__array_struct__
+ self.assertEqual(len(success), 0)
+ c = v.__array_struct__
+ self.assertEqual(len(success), 0)
+ c = v = None
+ gc.collect()
+ self.assertEqual(len(success), 1)
+ self.assertTrue(success[0])
+
+ def test_attribute(self):
+ v = BufferProxy(self.view_keywords)
+ self.assertRaises(AttributeError, getattr, v, "undefined")
+ v.undefined = 12
+ self.assertEqual(v.undefined, 12)
+ del v.undefined
+ self.assertRaises(AttributeError, getattr, v, "undefined")
+
+ def test_weakref(self):
+ v = BufferProxy(self.view_keywords)
+ weak_v = weakref.ref(v)
+
+ self.assertIs(weak_v(), v)
+
+ v = None
+ gc.collect()
+
+ self.assertIsNone(weak_v())
+
+ def test_gc(self):
+ """refcount agnostic check that contained objects are freed"""
+
+ def before_callback(parent):
+ return r[0]
+
+ def after_callback(parent):
+ return r[1]
+
+ class Obj:
+ pass
+
+ p = Obj()
+ a = Obj()
+ r = [Obj(), Obj()]
+ weak_p = weakref.ref(p)
+ weak_a = weakref.ref(a)
+ weak_r0 = weakref.ref(r[0])
+ weak_r1 = weakref.ref(r[1])
+ weak_before = weakref.ref(before_callback)
+ weak_after = weakref.ref(after_callback)
+ kwds = dict(self.view_keywords)
+ kwds["parent"] = p
+ kwds["before"] = before_callback
+ kwds["after"] = after_callback
+ v = BufferProxy(kwds)
+ v.some_attribute = a
+ weak_v = weakref.ref(v)
+ kwds = p = a = before_callback = after_callback = None
+ gc.collect()
+ self.assertTrue(weak_p() is not None)
+ self.assertTrue(weak_a() is not None)
+ self.assertTrue(weak_before() is not None)
+ self.assertTrue(weak_after() is not None)
+ v = None
+ [gc.collect() for x in range(4)]
+ self.assertTrue(weak_v() is None)
+ self.assertTrue(weak_p() is None)
+ self.assertTrue(weak_a() is None)
+ self.assertTrue(weak_before() is None)
+ self.assertTrue(weak_after() is None)
+ self.assertTrue(weak_r0() is not None)
+ self.assertTrue(weak_r1() is not None)
+ r = None
+ gc.collect()
+ self.assertTrue(weak_r0() is None)
+ self.assertTrue(weak_r1() is None)
+
+ # Cycle removal
+ kwds = dict(self.view_keywords)
+ kwds["parent"] = []
+ v = BufferProxy(kwds)
+ v.some_attribute = v
+ tracked = True
+ for o in gc.get_objects():
+ if o is v:
+ break
+ else:
+ tracked = False
+ self.assertTrue(tracked)
+ kwds["parent"].append(v)
+ kwds = None
+ gc.collect()
+ n1 = len(gc.garbage)
+ v = None
+ gc.collect()
+ n2 = len(gc.garbage)
+ self.assertEqual(n2, n1)
+
+ def test_c_api(self):
+ api = pygame.bufferproxy._PYGAME_C_API
+ api_type = type(pygame.base._PYGAME_C_API)
+
+ self.assertIsInstance(api, api_type)
+
+ def test_repr(self):
+ v = BufferProxy(self.view_keywords)
+ cname = BufferProxy.__name__
+ oname, ovalue = re.findall(r"<([^)]+)\(([^)]+)\)>", repr(v))[0]
+ self.assertEqual(oname, cname)
+ self.assertEqual(v.length, int(ovalue))
+
+ def test_subclassing(self):
+ class MyBufferProxy(BufferProxy):
+ def __repr__(self):
+ return f"*{BufferProxy.__repr__(self)}*"
+
+ kwds = dict(self.view_keywords)
+ kwds["parent"] = 0
+ v = MyBufferProxy(kwds)
+ self.assertEqual(v.parent, 0)
+ r = repr(v)
+ self.assertEqual(r[:2], "*<")
+ self.assertEqual(r[-2:], ">*")
+
+ @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
+ def NEWBUF_test_newbuf(self):
+ from ctypes import string_at
+
+ from pygame.tests.test_utils import buftools
+
+ Exporter = buftools.Exporter
+ Importer = buftools.Importer
+ exp = Exporter((10,), "B", readonly=True)
+ b = BufferProxy(exp)
+ self.assertEqual(b.length, exp.len)
+ self.assertEqual(b.raw, string_at(exp.buf, exp.len))
+ d = b.__array_interface__
+ try:
+ self.assertEqual(d["typestr"], "|u1")
+ self.assertEqual(d["shape"], exp.shape)
+ self.assertEqual(d["strides"], exp.strides)
+ self.assertEqual(d["data"], (exp.buf, True))
+ finally:
+ d = None
+ exp = Exporter((3,), "=h")
+ b = BufferProxy(exp)
+ self.assertEqual(b.length, exp.len)
+ self.assertEqual(b.raw, string_at(exp.buf, exp.len))
+ d = b.__array_interface__
+ try:
+ lil_endian = pygame.get_sdl_byteorder() == pygame.LIL_ENDIAN
+ f = f"{'<' if lil_endian else '>'}i{exp.itemsize}"
+ self.assertEqual(d["typestr"], f)
+ self.assertEqual(d["shape"], exp.shape)
+ self.assertEqual(d["strides"], exp.strides)
+ self.assertEqual(d["data"], (exp.buf, False))
+ finally:
+ d = None
+
+ exp = Exporter((10, 2), "=i")
+ b = BufferProxy(exp)
+ imp = Importer(b, buftools.PyBUF_RECORDS)
+ self.assertTrue(imp.obj is b)
+ self.assertEqual(imp.buf, exp.buf)
+ self.assertEqual(imp.ndim, exp.ndim)
+ self.assertEqual(imp.format, exp.format)
+ self.assertEqual(imp.readonly, exp.readonly)
+ self.assertEqual(imp.itemsize, exp.itemsize)
+ self.assertEqual(imp.len, exp.len)
+ self.assertEqual(imp.shape, exp.shape)
+ self.assertEqual(imp.strides, exp.strides)
+ self.assertTrue(imp.suboffsets is None)
+
+ d = {
+ "typestr": "|u1",
+ "shape": (10,),
+ "strides": (1,),
+ "data": (9, True),
+ } # 9? Will not reading the data anyway.
+ b = BufferProxy(d)
+ imp = Importer(b, buftools.PyBUF_SIMPLE)
+ self.assertTrue(imp.obj is b)
+ self.assertEqual(imp.buf, 9)
+ self.assertEqual(imp.len, 10)
+ self.assertEqual(imp.format, None)
+ self.assertEqual(imp.itemsize, 1)
+ self.assertEqual(imp.ndim, 0)
+ self.assertTrue(imp.readonly)
+ self.assertTrue(imp.shape is None)
+ self.assertTrue(imp.strides is None)
+ self.assertTrue(imp.suboffsets is None)
+
+ try:
+ pygame.bufferproxy.get_segcount
+ except AttributeError:
+ pass
+ else:
+
+ def test_oldbuf_arg(self):
+ self.OLDBUF_test_oldbuf_arg()
+
+ def OLDBUF_test_oldbuf_arg(self):
+ from pygame.bufferproxy import get_segcount, get_read_buffer, get_write_buffer
+
+ content = b"\x01\x00\x00\x02" * 12
+ memory = ctypes.create_string_buffer(content)
+ memaddr = ctypes.addressof(memory)
+
+ def raise_exception(o):
+ raise ValueError("An exception")
+
+ bf = BufferProxy(
+ {
+ "shape": (len(content),),
+ "typestr": "|u1",
+ "data": (memaddr, False),
+ "strides": (1,),
+ }
+ )
+ seglen, segaddr = get_read_buffer(bf, 0)
+ self.assertEqual(segaddr, 0)
+ self.assertEqual(seglen, 0)
+ seglen, segaddr = get_write_buffer(bf, 0)
+ self.assertEqual(segaddr, 0)
+ self.assertEqual(seglen, 0)
+ segcount, buflen = get_segcount(bf)
+ self.assertEqual(segcount, 1)
+ self.assertEqual(buflen, len(content))
+ seglen, segaddr = get_read_buffer(bf, 0)
+ self.assertEqual(segaddr, memaddr)
+ self.assertEqual(seglen, len(content))
+ seglen, segaddr = get_write_buffer(bf, 0)
+ self.assertEqual(segaddr, memaddr)
+ self.assertEqual(seglen, len(content))
+
+ bf = BufferProxy(
+ {
+ "shape": (len(content),),
+ "typestr": "|u1",
+ "data": (memaddr, True),
+ "strides": (1,),
+ }
+ )
+ segcount, buflen = get_segcount(bf)
+ self.assertEqual(segcount, 1)
+ self.assertEqual(buflen, len(content))
+ seglen, segaddr = get_read_buffer(bf, 0)
+ self.assertEqual(segaddr, memaddr)
+ self.assertEqual(seglen, len(content))
+ self.assertRaises(ValueError, get_write_buffer, bf, 0)
+
+ bf = BufferProxy(
+ {
+ "shape": (len(content),),
+ "typestr": "|u1",
+ "data": (memaddr, True),
+ "strides": (1,),
+ "before": raise_exception,
+ }
+ )
+ segcount, buflen = get_segcount(bf)
+ self.assertEqual(segcount, 0)
+ self.assertEqual(buflen, 0)
+
+ bf = BufferProxy(
+ {
+ "shape": (3, 4),
+ "typestr": "|u4",
+ "data": (memaddr, True),
+ "strides": (12, 4),
+ }
+ )
+ segcount, buflen = get_segcount(bf)
+ self.assertEqual(segcount, 3 * 4)
+ self.assertEqual(buflen, 3 * 4 * 4)
+ for i in range(0, 4):
+ seglen, segaddr = get_read_buffer(bf, i)
+ self.assertEqual(segaddr, memaddr + i * 4)
+ self.assertEqual(seglen, 4)
+
+
+class BufferProxyLegacyTest(unittest.TestCase):
+ content = b"\x01\x00\x00\x02" * 12
+ buffer = ctypes.create_string_buffer(content)
+ data = (ctypes.addressof(buffer), True)
+
+ def test_length(self):
+ # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.length:
+
+ # The size of the buffer data in bytes.
+ bf = BufferProxy(
+ {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (12, 4)}
+ )
+ self.assertEqual(bf.length, len(self.content))
+ bf = BufferProxy(
+ {"shape": (3, 3), "typestr": "|u4", "data": self.data, "strides": (12, 4)}
+ )
+ self.assertEqual(bf.length, 3 * 3 * 4)
+
+ def test_raw(self):
+ # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.raw:
+
+ # The raw buffer data as string. The string may contain NUL bytes.
+
+ bf = BufferProxy(
+ {"shape": (len(self.content),), "typestr": "|u1", "data": self.data}
+ )
+ self.assertEqual(bf.raw, self.content)
+ bf = BufferProxy(
+ {"shape": (3, 4), "typestr": "|u4", "data": self.data, "strides": (4, 12)}
+ )
+ self.assertEqual(bf.raw, self.content)
+ bf = BufferProxy(
+ {"shape": (3, 4), "typestr": "|u1", "data": self.data, "strides": (16, 4)}
+ )
+ self.assertRaises(ValueError, getattr, bf, "raw")
+
+ def test_write(self):
+ # __doc__ (as of 2008-08-02) for pygame.bufferproxy.BufferProxy.write:
+
+ # B.write (bufferproxy, buffer, offset) -> None
+ #
+ # Writes raw data to the bufferproxy.
+ #
+ # Writes the raw data from buffer to the BufferProxy object, starting
+ # at the specified offset within the BufferProxy.
+ # If the length of the passed buffer exceeds the length of the
+ # BufferProxy (reduced by the offset), an IndexError will be raised.
+ from ctypes import c_byte, sizeof, addressof, string_at, memset
+
+ nullbyte = b"\x00"
+ Buf = c_byte * 10
+ data_buf = Buf(*range(1, 3 * sizeof(Buf) + 1, 3))
+ data = string_at(data_buf, sizeof(data_buf))
+ buf = Buf()
+ bp = BufferProxy(
+ {"typestr": "|u1", "shape": (sizeof(buf),), "data": (addressof(buf), False)}
+ )
+ try:
+ self.assertEqual(bp.raw, nullbyte * sizeof(Buf))
+ bp.write(data)
+ self.assertEqual(bp.raw, data)
+ memset(buf, 0, sizeof(buf))
+ bp.write(data[:3], 2)
+ raw = bp.raw
+ self.assertEqual(raw[:2], nullbyte * 2)
+ self.assertEqual(raw[2:5], data[:3])
+ self.assertEqual(raw[5:], nullbyte * (sizeof(Buf) - 5))
+ bp.write(data[:3], bp.length - 3)
+ raw = bp.raw
+ self.assertEqual(raw[-3:], data[:3])
+ self.assertRaises(IndexError, bp.write, data, 1)
+ self.assertRaises(IndexError, bp.write, data[:5], -1)
+ self.assertRaises(IndexError, bp.write, data[:5], bp.length)
+ self.assertRaises(TypeError, bp.write, 12)
+ bp = BufferProxy(
+ {
+ "typestr": "|u1",
+ "shape": (sizeof(buf),),
+ "data": (addressof(buf), True),
+ }
+ )
+ self.assertRaises(pygame.BufferError, bp.write, b"123")
+ finally:
+ # Make sure bp is garbage collected before buf
+ bp = None
+ gc.collect()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/camera_test.py b/laplas/abstract_map/pygame/tests/camera_test.py
new file mode 100644
index 00000000..c15d4f0c
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/camera_test.py
@@ -0,0 +1,35 @@
+import unittest
+import os
+import pygame
+import pygame.camera
+
+
+class CameraModuleTest(unittest.TestCase):
+ def setUp(self):
+ pygame.init()
+
+ pygame.camera.init()
+
+ @unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") in ["dummy", "android"],
+ "requires the SDL_VIDEODRIVER to be non dummy",
+ )
+ def test_camera(self):
+ cameras = pygame.camera.list_cameras()
+
+ if len(cameras) == 0:
+ self.skipTest("No cameras found")
+
+ cam = pygame.camera.Camera(cameras[0], (640, 480))
+ cam.start()
+ image = cam.get_image()
+ self.assertIsNotNone(image, "Could not capture image")
+ cam.stop()
+
+ def tearDown(self):
+ pygame.camera.quit()
+ pygame.quit()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/color_test.py b/laplas/abstract_map/pygame/tests/color_test.py
new file mode 100644
index 00000000..eee5c11d
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/color_test.py
@@ -0,0 +1,1360 @@
+import math
+import operator
+import platform
+import unittest
+from collections.abc import Collection, Sequence
+
+import pygame
+from pygame.colordict import THECOLORS
+
+IS_PYPY = "PyPy" == platform.python_implementation()
+################################### CONSTANTS ##################################
+
+rgba_vals = [0, 1, 62, 63, 126, 127, 255]
+
+rgba_combinations = [
+ (r, g, b, a)
+ for r in rgba_vals
+ for g in rgba_vals
+ for b in rgba_vals
+ for a in rgba_vals
+]
+
+################################################################################
+
+
+def rgba_combos_Color_generator():
+ for rgba in rgba_combinations:
+ yield pygame.Color(*rgba)
+
+
+# Python gamma correct
+def gamma_correct(rgba_0_255, gamma):
+ corrected = round(255.0 * math.pow(rgba_0_255 / 255.0, gamma))
+ return max(min(int(corrected), 255), 0)
+
+
+################################################################################
+
+# TODO: add tests for
+# correct_gamma() -- test against statically defined verified correct values
+# coerce () -- ??
+
+
+def _assignr(x, y):
+ x.r = y
+
+
+def _assigng(x, y):
+ x.g = y
+
+
+def _assignb(x, y):
+ x.b = y
+
+
+def _assigna(x, y):
+ x.a = y
+
+
+def _assign_item(x, p, y):
+ x[p] = y
+
+
+class ColorTypeTest(unittest.TestCase):
+ def test_new(self):
+ c = pygame.Color.__new__(pygame.Color)
+ self.assertEqual(c, pygame.Color(0, 0, 0, 255))
+ self.assertEqual(len(c), 4)
+
+ def test_init(self):
+ c = pygame.Color(10, 20, 30, 200)
+ self.assertEqual(c, (10, 20, 30, 200))
+ c.set_length(3)
+ self.assertEqual(len(c), 3)
+ c.__init__(100, 110, 120, 128)
+ self.assertEqual(len(c), 4)
+ self.assertEqual(c, (100, 110, 120, 128))
+
+ def test_invalid_html_hex_codes(self):
+ # This was a problem with the way 2 digit hex numbers were
+ # calculated. The test_hex_digits test is related to the fix.
+ Color = pygame.color.Color
+ self.assertRaises(ValueError, lambda: Color("# f000000"))
+ self.assertRaises(ValueError, lambda: Color("#f 000000"))
+ self.assertRaises(ValueError, lambda: Color("#-f000000"))
+
+ def test_hex_digits(self):
+ # This is an implementation specific test.
+ # Two digit hex numbers are calculated using table lookups
+ # for the upper and lower digits.
+ Color = pygame.color.Color
+ self.assertEqual(Color("#00000000").r, 0x00)
+ self.assertEqual(Color("#10000000").r, 0x10)
+ self.assertEqual(Color("#20000000").r, 0x20)
+ self.assertEqual(Color("#30000000").r, 0x30)
+ self.assertEqual(Color("#40000000").r, 0x40)
+ self.assertEqual(Color("#50000000").r, 0x50)
+ self.assertEqual(Color("#60000000").r, 0x60)
+ self.assertEqual(Color("#70000000").r, 0x70)
+ self.assertEqual(Color("#80000000").r, 0x80)
+ self.assertEqual(Color("#90000000").r, 0x90)
+ self.assertEqual(Color("#A0000000").r, 0xA0)
+ self.assertEqual(Color("#B0000000").r, 0xB0)
+ self.assertEqual(Color("#C0000000").r, 0xC0)
+ self.assertEqual(Color("#D0000000").r, 0xD0)
+ self.assertEqual(Color("#E0000000").r, 0xE0)
+ self.assertEqual(Color("#F0000000").r, 0xF0)
+ self.assertEqual(Color("#01000000").r, 0x01)
+ self.assertEqual(Color("#02000000").r, 0x02)
+ self.assertEqual(Color("#03000000").r, 0x03)
+ self.assertEqual(Color("#04000000").r, 0x04)
+ self.assertEqual(Color("#05000000").r, 0x05)
+ self.assertEqual(Color("#06000000").r, 0x06)
+ self.assertEqual(Color("#07000000").r, 0x07)
+ self.assertEqual(Color("#08000000").r, 0x08)
+ self.assertEqual(Color("#09000000").r, 0x09)
+ self.assertEqual(Color("#0A000000").r, 0x0A)
+ self.assertEqual(Color("#0B000000").r, 0x0B)
+ self.assertEqual(Color("#0C000000").r, 0x0C)
+ self.assertEqual(Color("#0D000000").r, 0x0D)
+ self.assertEqual(Color("#0E000000").r, 0x0E)
+ self.assertEqual(Color("#0F000000").r, 0x0F)
+
+ def test_comparison(self):
+ Color = pygame.color.Color
+
+ # Check valid comparisons
+ self.assertTrue(Color(255, 0, 0, 0) == Color(255, 0, 0, 0))
+ self.assertTrue(Color(0, 255, 0, 0) == Color(0, 255, 0, 0))
+ self.assertTrue(Color(0, 0, 255, 0) == Color(0, 0, 255, 0))
+ self.assertTrue(Color(0, 0, 0, 255) == Color(0, 0, 0, 255))
+ self.assertFalse(Color(0, 0, 0, 0) == Color(255, 0, 0, 0))
+ self.assertFalse(Color(0, 0, 0, 0) == Color(0, 255, 0, 0))
+ self.assertFalse(Color(0, 0, 0, 0) == Color(0, 0, 255, 0))
+ self.assertFalse(Color(0, 0, 0, 0) == Color(0, 0, 0, 255))
+ self.assertTrue(Color(0, 0, 0, 0) != Color(255, 0, 0, 0))
+ self.assertTrue(Color(0, 0, 0, 0) != Color(0, 255, 0, 0))
+ self.assertTrue(Color(0, 0, 0, 0) != Color(0, 0, 255, 0))
+ self.assertTrue(Color(0, 0, 0, 0) != Color(0, 0, 0, 255))
+ self.assertFalse(Color(255, 0, 0, 0) != Color(255, 0, 0, 0))
+ self.assertFalse(Color(0, 255, 0, 0) != Color(0, 255, 0, 0))
+ self.assertFalse(Color(0, 0, 255, 0) != Color(0, 0, 255, 0))
+ self.assertFalse(Color(0, 0, 0, 255) != Color(0, 0, 0, 255))
+
+ self.assertTrue(Color(255, 0, 0, 0) == (255, 0, 0, 0))
+ self.assertTrue(Color(0, 255, 0, 0) == (0, 255, 0, 0))
+ self.assertTrue(Color(0, 0, 255, 0) == (0, 0, 255, 0))
+ self.assertTrue(Color(0, 0, 0, 255) == (0, 0, 0, 255))
+ self.assertFalse(Color(0, 0, 0, 0) == (255, 0, 0, 0))
+ self.assertFalse(Color(0, 0, 0, 0) == (0, 255, 0, 0))
+ self.assertFalse(Color(0, 0, 0, 0) == (0, 0, 255, 0))
+ self.assertFalse(Color(0, 0, 0, 0) == (0, 0, 0, 255))
+ self.assertTrue(Color(0, 0, 0, 0) != (255, 0, 0, 0))
+ self.assertTrue(Color(0, 0, 0, 0) != (0, 255, 0, 0))
+ self.assertTrue(Color(0, 0, 0, 0) != (0, 0, 255, 0))
+ self.assertTrue(Color(0, 0, 0, 0) != (0, 0, 0, 255))
+ self.assertFalse(Color(255, 0, 0, 0) != (255, 0, 0, 0))
+ self.assertFalse(Color(0, 255, 0, 0) != (0, 255, 0, 0))
+ self.assertFalse(Color(0, 0, 255, 0) != (0, 0, 255, 0))
+ self.assertFalse(Color(0, 0, 0, 255) != (0, 0, 0, 255))
+
+ self.assertTrue((255, 0, 0, 0) == Color(255, 0, 0, 0))
+ self.assertTrue((0, 255, 0, 0) == Color(0, 255, 0, 0))
+ self.assertTrue((0, 0, 255, 0) == Color(0, 0, 255, 0))
+ self.assertTrue((0, 0, 0, 255) == Color(0, 0, 0, 255))
+ self.assertFalse((0, 0, 0, 0) == Color(255, 0, 0, 0))
+ self.assertFalse((0, 0, 0, 0) == Color(0, 255, 0, 0))
+ self.assertFalse((0, 0, 0, 0) == Color(0, 0, 255, 0))
+ self.assertFalse((0, 0, 0, 0) == Color(0, 0, 0, 255))
+ self.assertTrue((0, 0, 0, 0) != Color(255, 0, 0, 0))
+ self.assertTrue((0, 0, 0, 0) != Color(0, 255, 0, 0))
+ self.assertTrue((0, 0, 0, 0) != Color(0, 0, 255, 0))
+ self.assertTrue((0, 0, 0, 0) != Color(0, 0, 0, 255))
+ self.assertFalse((255, 0, 0, 0) != Color(255, 0, 0, 0))
+ self.assertFalse((0, 255, 0, 0) != Color(0, 255, 0, 0))
+ self.assertFalse((0, 0, 255, 0) != Color(0, 0, 255, 0))
+ self.assertFalse((0, 0, 0, 255) != Color(0, 0, 0, 255))
+
+ class TupleSubclass(tuple):
+ pass
+
+ self.assertTrue(Color(255, 0, 0, 0) == TupleSubclass((255, 0, 0, 0)))
+ self.assertTrue(TupleSubclass((255, 0, 0, 0)) == Color(255, 0, 0, 0))
+ self.assertFalse(Color(255, 0, 0, 0) != TupleSubclass((255, 0, 0, 0)))
+ self.assertFalse(TupleSubclass((255, 0, 0, 0)) != Color(255, 0, 0, 0))
+
+ # These are not supported so will be unequal.
+ self.assertFalse(Color(255, 0, 0, 0) == "#ff000000")
+ self.assertTrue(Color(255, 0, 0, 0) != "#ff000000")
+
+ self.assertFalse("#ff000000" == Color(255, 0, 0, 0))
+ self.assertTrue("#ff000000" != Color(255, 0, 0, 0))
+
+ self.assertFalse(Color(255, 0, 0, 0) == 0xFF000000)
+ self.assertTrue(Color(255, 0, 0, 0) != 0xFF000000)
+
+ self.assertFalse(0xFF000000 == Color(255, 0, 0, 0))
+ self.assertTrue(0xFF000000 != Color(255, 0, 0, 0))
+
+ self.assertFalse(Color(255, 0, 0, 0) == [255, 0, 0, 0])
+ self.assertTrue(Color(255, 0, 0, 0) != [255, 0, 0, 0])
+
+ self.assertFalse([255, 0, 0, 0] == Color(255, 0, 0, 0))
+ self.assertTrue([255, 0, 0, 0] != Color(255, 0, 0, 0))
+
+ # Comparison is not implemented for invalid color values.
+ class Test:
+ def __eq__(self, other):
+ return -1
+
+ def __ne__(self, other):
+ return -2
+
+ class TestTuple(tuple):
+ def __eq__(self, other):
+ return -1
+
+ def __ne__(self, other):
+ return -2
+
+ t = Test()
+ t_tuple = TestTuple(("a", 0, 0, 0))
+ black = Color("black")
+ self.assertEqual(black == t, -1)
+ self.assertEqual(t == black, -1)
+ self.assertEqual(black != t, -2)
+ self.assertEqual(t != black, -2)
+ self.assertEqual(black == t_tuple, -1)
+ self.assertEqual(black != t_tuple, -2)
+ self.assertEqual(t_tuple == black, -1)
+ self.assertEqual(t_tuple != black, -2)
+
+ def test_ignore_whitespace(self):
+ self.assertEqual(pygame.color.Color("red"), pygame.color.Color(" r e d "))
+
+ def test_slice(self):
+ # """|tags: python3_ignore|"""
+
+ # slicing a color gives you back a tuple.
+ # do all sorts of slice combinations.
+ c = pygame.Color(1, 2, 3, 4)
+
+ self.assertEqual((1, 2, 3, 4), c[:])
+ self.assertEqual((1, 2, 3), c[:-1])
+
+ self.assertEqual((), c[:-5])
+
+ self.assertEqual((1, 2, 3, 4), c[:4])
+ self.assertEqual((1, 2, 3, 4), c[:5])
+ self.assertEqual((1, 2), c[:2])
+ self.assertEqual((1,), c[:1])
+ self.assertEqual((), c[:0])
+
+ self.assertEqual((2,), c[1:-2])
+ self.assertEqual((3, 4), c[-2:])
+ self.assertEqual((4,), c[-1:])
+
+ # NOTE: assigning to a slice is currently unsupported.
+
+ def test_unpack(self):
+ # should be able to unpack to r,g,b,a and r,g,b
+ c = pygame.Color(1, 2, 3, 4)
+ r, g, b, a = c
+ self.assertEqual((1, 2, 3, 4), (r, g, b, a))
+ self.assertEqual(c, (r, g, b, a))
+
+ c.set_length(3)
+ r, g, b = c
+ self.assertEqual((1, 2, 3), (r, g, b))
+
+ # Checking if DeprecationWarning is triggered
+ # when function is called
+ for i in range(1, 5):
+ with self.assertWarns(DeprecationWarning):
+ c.set_length(i)
+
+ def test_length(self):
+ # should be able to unpack to r,g,b,a and r,g,b
+ c = pygame.Color(1, 2, 3, 4)
+ self.assertEqual(len(c), 4)
+
+ c.set_length(3)
+ self.assertEqual(len(c), 3)
+
+ # it keeps the old alpha anyway...
+ self.assertEqual(c.a, 4)
+
+ # however you can't get the alpha in this way:
+ self.assertRaises(IndexError, lambda x: c[x], 4)
+
+ c.set_length(4)
+ self.assertEqual(len(c), 4)
+ self.assertEqual(len(c), 4)
+
+ self.assertRaises(ValueError, c.set_length, 5)
+ self.assertRaises(ValueError, c.set_length, -1)
+ self.assertRaises(ValueError, c.set_length, 0)
+ self.assertRaises(ValueError, c.set_length, pow(2, 33))
+
+ def test_case_insensitivity_of_string_args(self):
+ self.assertEqual(pygame.color.Color("red"), pygame.color.Color("Red"))
+
+ def test_color(self):
+ """Ensures Color objects can be created."""
+ color = pygame.Color(0, 0, 0, 0)
+
+ self.assertIsInstance(color, pygame.Color)
+
+ def test_color__rgba_int_args(self):
+ """Ensures Color objects can be created using ints."""
+ color = pygame.Color(10, 20, 30, 40)
+
+ self.assertEqual(color.r, 10)
+ self.assertEqual(color.g, 20)
+ self.assertEqual(color.b, 30)
+ self.assertEqual(color.a, 40)
+
+ def test_color__rgba_int_args_without_alpha(self):
+ """Ensures Color objects can be created without providing alpha."""
+ color = pygame.Color(10, 20, 30)
+
+ self.assertEqual(color.r, 10)
+ self.assertEqual(color.g, 20)
+ self.assertEqual(color.b, 30)
+ self.assertEqual(color.a, 255)
+
+ def test_color__rgba_int_args_invalid_value(self):
+ """Ensures invalid values are detected when creating Color objects."""
+ self.assertRaises(ValueError, pygame.Color, 257, 10, 105, 44)
+ self.assertRaises(ValueError, pygame.Color, 10, 257, 105, 44)
+ self.assertRaises(ValueError, pygame.Color, 10, 105, 257, 44)
+ self.assertRaises(ValueError, pygame.Color, 10, 105, 44, 257)
+
+ def test_color__rgba_int_args_invalid_value_without_alpha(self):
+ """Ensures invalid values are detected when creating Color objects
+ without providing an alpha.
+ """
+ self.assertRaises(ValueError, pygame.Color, 256, 10, 105)
+ self.assertRaises(ValueError, pygame.Color, 10, 256, 105)
+ self.assertRaises(ValueError, pygame.Color, 10, 105, 256)
+
+ def test_color__color_object_arg(self):
+ """Ensures Color objects can be created using Color objects."""
+ color_args = (10, 20, 30, 40)
+ color_obj = pygame.Color(*color_args)
+
+ new_color_obj = pygame.Color(color_obj)
+
+ self.assertIsInstance(new_color_obj, pygame.Color)
+ self.assertEqual(new_color_obj, color_obj)
+ self.assertEqual(new_color_obj.r, color_args[0])
+ self.assertEqual(new_color_obj.g, color_args[1])
+ self.assertEqual(new_color_obj.b, color_args[2])
+ self.assertEqual(new_color_obj.a, color_args[3])
+
+ def test_color__name_str_arg(self):
+ """Ensures Color objects can be created using str names."""
+ for name in ("aquamarine3", "AQUAMARINE3", "AqUAmArIne3"):
+ color = pygame.Color(name)
+
+ self.assertEqual(color.r, 102)
+ self.assertEqual(color.g, 205)
+ self.assertEqual(color.b, 170)
+ self.assertEqual(color.a, 255)
+
+ def test_color__name_str_arg_from_colordict(self):
+ """Ensures Color objects can be created using str names
+ from the THECOLORS dict."""
+ for name, values in THECOLORS.items():
+ color = pygame.Color(name)
+
+ self.assertEqual(color.r, values[0])
+ self.assertEqual(color.g, values[1])
+ self.assertEqual(color.b, values[2])
+ self.assertEqual(color.a, values[3])
+
+ def test_color__html_str_arg(self):
+ """Ensures Color objects can be created using html strings."""
+ # See test_webstyle() for related tests.
+ color = pygame.Color("#a1B2c3D4")
+
+ self.assertEqual(color.r, 0xA1)
+ self.assertEqual(color.g, 0xB2)
+ self.assertEqual(color.b, 0xC3)
+ self.assertEqual(color.a, 0xD4)
+
+ def test_color__hex_str_arg(self):
+ """Ensures Color objects can be created using hex strings."""
+ # See test_webstyle() for related tests.
+ color = pygame.Color("0x1a2B3c4D")
+
+ self.assertEqual(color.r, 0x1A)
+ self.assertEqual(color.g, 0x2B)
+ self.assertEqual(color.b, 0x3C)
+ self.assertEqual(color.a, 0x4D)
+
+ def test_color__int_arg(self):
+ """Ensures Color objects can be created using one int value."""
+ for value in (0x0, 0xFFFFFFFF, 0xAABBCCDD):
+ color = pygame.Color(value)
+
+ self.assertEqual(color.r, (value >> 24) & 0xFF)
+ self.assertEqual(color.g, (value >> 16) & 0xFF)
+ self.assertEqual(color.b, (value >> 8) & 0xFF)
+ self.assertEqual(color.a, value & 0xFF)
+
+ def test_color__int_arg_invalid(self):
+ """Ensures invalid int values are detected when creating Color objects."""
+ with self.assertRaises(ValueError):
+ color = pygame.Color(0x1FFFFFFFF)
+
+ def test_color__sequence_arg(self):
+ """Ensures Color objects can be created using tuples/lists."""
+ color_values = (33, 44, 55, 66)
+ for seq_type in (tuple, list):
+ color = pygame.Color(seq_type(color_values))
+
+ self.assertEqual(color.r, color_values[0])
+ self.assertEqual(color.g, color_values[1])
+ self.assertEqual(color.b, color_values[2])
+ self.assertEqual(color.a, color_values[3])
+
+ def test_color__sequence_arg_without_alpha(self):
+ """Ensures Color objects can be created using tuples/lists
+ without providing an alpha value.
+ """
+ color_values = (33, 44, 55)
+ for seq_type in (tuple, list):
+ color = pygame.Color(seq_type(color_values))
+
+ self.assertEqual(color.r, color_values[0])
+ self.assertEqual(color.g, color_values[1])
+ self.assertEqual(color.b, color_values[2])
+ self.assertEqual(color.a, 255)
+
+ def test_color__sequence_arg_invalid_value(self):
+ """Ensures invalid sequences are detected when creating Color objects."""
+ cls = pygame.Color
+ for seq_type in (tuple, list):
+ self.assertRaises(ValueError, cls, seq_type((256, 90, 80, 70)))
+ self.assertRaises(ValueError, cls, seq_type((100, 256, 80, 70)))
+ self.assertRaises(ValueError, cls, seq_type((100, 90, 256, 70)))
+ self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 256)))
+
+ def test_color__sequence_arg_invalid_value_without_alpha(self):
+ """Ensures invalid sequences are detected when creating Color objects
+ without providing an alpha.
+ """
+ cls = pygame.Color
+ for seq_type in (tuple, list):
+ self.assertRaises(ValueError, cls, seq_type((256, 90, 80)))
+ self.assertRaises(ValueError, cls, seq_type((100, 256, 80)))
+ self.assertRaises(ValueError, cls, seq_type((100, 90, 256)))
+
+ def test_color__sequence_arg_invalid_format(self):
+ """Ensures invalid sequences are detected when creating Color objects
+ with the wrong number of values.
+ """
+ cls = pygame.Color
+ for seq_type in (tuple, list):
+ self.assertRaises(ValueError, cls, seq_type((100,)))
+ self.assertRaises(ValueError, cls, seq_type((100, 90)))
+ self.assertRaises(ValueError, cls, seq_type((100, 90, 80, 70, 60)))
+
+ def test_rgba(self):
+ c = pygame.Color(0)
+ self.assertEqual(c.r, 0)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 0)
+ self.assertEqual(c.a, 0)
+
+ # Test simple assignments
+ c.r = 123
+ self.assertEqual(c.r, 123)
+ self.assertRaises(ValueError, _assignr, c, 537)
+ self.assertEqual(c.r, 123)
+ self.assertRaises(ValueError, _assignr, c, -3)
+ self.assertEqual(c.r, 123)
+
+ c.g = 55
+ self.assertEqual(c.g, 55)
+ self.assertRaises(ValueError, _assigng, c, 348)
+ self.assertEqual(c.g, 55)
+ self.assertRaises(ValueError, _assigng, c, -44)
+ self.assertEqual(c.g, 55)
+
+ c.b = 77
+ self.assertEqual(c.b, 77)
+ self.assertRaises(ValueError, _assignb, c, 256)
+ self.assertEqual(c.b, 77)
+ self.assertRaises(ValueError, _assignb, c, -12)
+ self.assertEqual(c.b, 77)
+
+ c.a = 255
+ self.assertEqual(c.a, 255)
+ self.assertRaises(ValueError, _assigna, c, 312)
+ self.assertEqual(c.a, 255)
+ self.assertRaises(ValueError, _assigna, c, -10)
+ self.assertEqual(c.a, 255)
+
+ def test_repr(self):
+ c = pygame.Color(68, 38, 26, 69)
+ t = "(68, 38, 26, 69)"
+ self.assertEqual(repr(c), t)
+
+ def test_add(self):
+ c1 = pygame.Color(0)
+ self.assertEqual(c1.r, 0)
+ self.assertEqual(c1.g, 0)
+ self.assertEqual(c1.b, 0)
+ self.assertEqual(c1.a, 0)
+
+ c2 = pygame.Color(20, 33, 82, 193)
+ self.assertEqual(c2.r, 20)
+ self.assertEqual(c2.g, 33)
+ self.assertEqual(c2.b, 82)
+ self.assertEqual(c2.a, 193)
+
+ c3 = c1 + c2
+ self.assertEqual(c3.r, 20)
+ self.assertEqual(c3.g, 33)
+ self.assertEqual(c3.b, 82)
+ self.assertEqual(c3.a, 193)
+
+ c3 = c3 + c2
+ self.assertEqual(c3.r, 40)
+ self.assertEqual(c3.g, 66)
+ self.assertEqual(c3.b, 164)
+ self.assertEqual(c3.a, 255)
+
+ # Issue #286: Is type checking done for Python 3.x?
+ self.assertRaises(TypeError, operator.add, c1, None)
+ self.assertRaises(TypeError, operator.add, None, c1)
+
+ def test_sub(self):
+ c1 = pygame.Color(0xFFFFFFFF)
+ self.assertEqual(c1.r, 255)
+ self.assertEqual(c1.g, 255)
+ self.assertEqual(c1.b, 255)
+ self.assertEqual(c1.a, 255)
+
+ c2 = pygame.Color(20, 33, 82, 193)
+ self.assertEqual(c2.r, 20)
+ self.assertEqual(c2.g, 33)
+ self.assertEqual(c2.b, 82)
+ self.assertEqual(c2.a, 193)
+
+ c3 = c1 - c2
+ self.assertEqual(c3.r, 235)
+ self.assertEqual(c3.g, 222)
+ self.assertEqual(c3.b, 173)
+ self.assertEqual(c3.a, 62)
+
+ c3 = c3 - c2
+ self.assertEqual(c3.r, 215)
+ self.assertEqual(c3.g, 189)
+ self.assertEqual(c3.b, 91)
+ self.assertEqual(c3.a, 0)
+
+ # Issue #286: Is type checking done for Python 3.x?
+ self.assertRaises(TypeError, operator.sub, c1, None)
+ self.assertRaises(TypeError, operator.sub, None, c1)
+
+ def test_mul(self):
+ c1 = pygame.Color(0x01010101)
+ self.assertEqual(c1.r, 1)
+ self.assertEqual(c1.g, 1)
+ self.assertEqual(c1.b, 1)
+ self.assertEqual(c1.a, 1)
+
+ c2 = pygame.Color(2, 5, 3, 22)
+ self.assertEqual(c2.r, 2)
+ self.assertEqual(c2.g, 5)
+ self.assertEqual(c2.b, 3)
+ self.assertEqual(c2.a, 22)
+
+ c3 = c1 * c2
+ self.assertEqual(c3.r, 2)
+ self.assertEqual(c3.g, 5)
+ self.assertEqual(c3.b, 3)
+ self.assertEqual(c3.a, 22)
+
+ c3 = c3 * c2
+ self.assertEqual(c3.r, 4)
+ self.assertEqual(c3.g, 25)
+ self.assertEqual(c3.b, 9)
+ self.assertEqual(c3.a, 255)
+
+ # Issue #286: Is type checking done for Python 3.x?
+ self.assertRaises(TypeError, operator.mul, c1, None)
+ self.assertRaises(TypeError, operator.mul, None, c1)
+
+ def test_div(self):
+ c1 = pygame.Color(0x80808080)
+ self.assertEqual(c1.r, 128)
+ self.assertEqual(c1.g, 128)
+ self.assertEqual(c1.b, 128)
+ self.assertEqual(c1.a, 128)
+
+ c2 = pygame.Color(2, 4, 8, 16)
+ self.assertEqual(c2.r, 2)
+ self.assertEqual(c2.g, 4)
+ self.assertEqual(c2.b, 8)
+ self.assertEqual(c2.a, 16)
+
+ c3 = c1 // c2
+ self.assertEqual(c3.r, 64)
+ self.assertEqual(c3.g, 32)
+ self.assertEqual(c3.b, 16)
+ self.assertEqual(c3.a, 8)
+
+ c3 = c3 // c2
+ self.assertEqual(c3.r, 32)
+ self.assertEqual(c3.g, 8)
+ self.assertEqual(c3.b, 2)
+ self.assertEqual(c3.a, 0)
+
+ # Issue #286: Is type checking done for Python 3.x?
+ self.assertRaises(TypeError, operator.floordiv, c1, None)
+ self.assertRaises(TypeError, operator.floordiv, None, c1)
+
+ # Division by zero check
+ dividend = pygame.Color(255, 255, 255, 255)
+ for i in range(4):
+ divisor = pygame.Color(64, 64, 64, 64)
+ divisor[i] = 0
+ quotient = pygame.Color(3, 3, 3, 3)
+ quotient[i] = 0
+ self.assertEqual(dividend // divisor, quotient)
+
+ def test_mod(self):
+ c1 = pygame.Color(0xFFFFFFFF)
+ self.assertEqual(c1.r, 255)
+ self.assertEqual(c1.g, 255)
+ self.assertEqual(c1.b, 255)
+ self.assertEqual(c1.a, 255)
+
+ c2 = pygame.Color(2, 4, 8, 16)
+ self.assertEqual(c2.r, 2)
+ self.assertEqual(c2.g, 4)
+ self.assertEqual(c2.b, 8)
+ self.assertEqual(c2.a, 16)
+
+ c3 = c1 % c2
+ self.assertEqual(c3.r, 1)
+ self.assertEqual(c3.g, 3)
+ self.assertEqual(c3.b, 7)
+ self.assertEqual(c3.a, 15)
+
+ # Issue #286: Is type checking done for Python 3.x?
+ self.assertRaises(TypeError, operator.mod, c1, None)
+ self.assertRaises(TypeError, operator.mod, None, c1)
+
+ # Division by zero check
+ dividend = pygame.Color(255, 255, 255, 255)
+ for i in range(4):
+ divisor = pygame.Color(64, 64, 64, 64)
+ divisor[i] = 0
+ quotient = pygame.Color(63, 63, 63, 63)
+ quotient[i] = 0
+ self.assertEqual(dividend % divisor, quotient)
+
+ def test_float(self):
+ c = pygame.Color(0xCC00CC00)
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 0)
+ self.assertEqual(float(c), float(0xCC00CC00))
+
+ c = pygame.Color(0x33727592)
+ self.assertEqual(c.r, 51)
+ self.assertEqual(c.g, 114)
+ self.assertEqual(c.b, 117)
+ self.assertEqual(c.a, 146)
+ self.assertEqual(float(c), float(0x33727592))
+
+ def test_oct(self):
+ c = pygame.Color(0xCC00CC00)
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 0)
+ self.assertEqual(oct(c), oct(0xCC00CC00))
+
+ c = pygame.Color(0x33727592)
+ self.assertEqual(c.r, 51)
+ self.assertEqual(c.g, 114)
+ self.assertEqual(c.b, 117)
+ self.assertEqual(c.a, 146)
+ self.assertEqual(oct(c), oct(0x33727592))
+
+ def test_hex(self):
+ c = pygame.Color(0xCC00CC00)
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 0)
+ self.assertEqual(hex(c), hex(0xCC00CC00))
+
+ c = pygame.Color(0x33727592)
+ self.assertEqual(c.r, 51)
+ self.assertEqual(c.g, 114)
+ self.assertEqual(c.b, 117)
+ self.assertEqual(c.a, 146)
+ self.assertEqual(hex(c), hex(0x33727592))
+
+ def test_webstyle(self):
+ c = pygame.Color("#CC00CC11")
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 17)
+ self.assertEqual(hex(c), hex(0xCC00CC11))
+
+ c = pygame.Color("#CC00CC")
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 255)
+ self.assertEqual(hex(c), hex(0xCC00CCFF))
+
+ c = pygame.Color("0xCC00CC11")
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 17)
+ self.assertEqual(hex(c), hex(0xCC00CC11))
+
+ c = pygame.Color("0xCC00CC")
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 255)
+ self.assertEqual(hex(c), hex(0xCC00CCFF))
+
+ self.assertRaises(ValueError, pygame.Color, "#cc00qq")
+ self.assertRaises(ValueError, pygame.Color, "0xcc00qq")
+ self.assertRaises(ValueError, pygame.Color, "09abcdef")
+ self.assertRaises(ValueError, pygame.Color, "09abcde")
+ self.assertRaises(ValueError, pygame.Color, "quarky")
+
+ def test_int(self):
+ # This will be a long
+ c = pygame.Color(0xCC00CC00)
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 0)
+ self.assertEqual(int(c), int(0xCC00CC00))
+
+ # This will be an int
+ c = pygame.Color(0x33727592)
+ self.assertEqual(c.r, 51)
+ self.assertEqual(c.g, 114)
+ self.assertEqual(c.b, 117)
+ self.assertEqual(c.a, 146)
+ self.assertEqual(int(c), int(0x33727592))
+
+ def test_long(self):
+ # This will be a long
+ c = pygame.Color(0xCC00CC00)
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 204)
+ self.assertEqual(c.a, 0)
+ self.assertEqual(int(c), int(0xCC00CC00))
+
+ # This will be an int
+ c = pygame.Color(0x33727592)
+ self.assertEqual(c.r, 51)
+ self.assertEqual(c.g, 114)
+ self.assertEqual(c.b, 117)
+ self.assertEqual(c.a, 146)
+ self.assertEqual(int(c), int(0x33727592))
+
+ def test_normalize(self):
+ c = pygame.Color(204, 38, 194, 55)
+ self.assertEqual(c.r, 204)
+ self.assertEqual(c.g, 38)
+ self.assertEqual(c.b, 194)
+ self.assertEqual(c.a, 55)
+
+ t = c.normalize()
+
+ self.assertAlmostEqual(t[0], 0.800000, 5)
+ self.assertAlmostEqual(t[1], 0.149016, 5)
+ self.assertAlmostEqual(t[2], 0.760784, 5)
+ self.assertAlmostEqual(t[3], 0.215686, 5)
+
+ def test_len(self):
+ c = pygame.Color(204, 38, 194, 55)
+ self.assertEqual(len(c), 4)
+
+ def test_get_item(self):
+ c = pygame.Color(204, 38, 194, 55)
+ self.assertEqual(c[0], 204)
+ self.assertEqual(c[1], 38)
+ self.assertEqual(c[2], 194)
+ self.assertEqual(c[3], 55)
+
+ def test_set_item(self):
+ c = pygame.Color(204, 38, 194, 55)
+ self.assertEqual(c[0], 204)
+ self.assertEqual(c[1], 38)
+ self.assertEqual(c[2], 194)
+ self.assertEqual(c[3], 55)
+
+ c[0] = 33
+ self.assertEqual(c[0], 33)
+ c[1] = 48
+ self.assertEqual(c[1], 48)
+ c[2] = 173
+ self.assertEqual(c[2], 173)
+ c[3] = 213
+ self.assertEqual(c[3], 213)
+
+ # Now try some 'invalid' ones
+ self.assertRaises(TypeError, _assign_item, c, 0, 95.485)
+ self.assertEqual(c[0], 33)
+ self.assertRaises(ValueError, _assign_item, c, 1, -83)
+ self.assertEqual(c[1], 48)
+ self.assertRaises(TypeError, _assign_item, c, 2, "Hello")
+ self.assertEqual(c[2], 173)
+
+ def test_Color_type_works_for_Surface_get_and_set_colorkey(self):
+ s = pygame.Surface((32, 32))
+
+ c = pygame.Color(33, 22, 11, 255)
+ s.set_colorkey(c)
+
+ get_r, get_g, get_b, get_a = s.get_colorkey()
+
+ self.assertTrue(get_r == c.r)
+ self.assertTrue(get_g == c.g)
+ self.assertTrue(get_b == c.b)
+ self.assertTrue(get_a == c.a)
+
+ ########## HSLA, HSVA, CMY, I1I2I3 ALL ELEMENTS WITHIN SPECIFIED RANGE #########
+
+ def test_hsla__all_elements_within_limits(self):
+ for c in rgba_combos_Color_generator():
+ h, s, l, a = c.hsla
+ self.assertTrue(0 <= h <= 360)
+ self.assertTrue(0 <= s <= 100)
+ self.assertTrue(0 <= l <= 100)
+ self.assertTrue(0 <= a <= 100)
+
+ def test_hsva__all_elements_within_limits(self):
+ for c in rgba_combos_Color_generator():
+ h, s, v, a = c.hsva
+ self.assertTrue(0 <= h <= 360)
+ self.assertTrue(0 <= s <= 100)
+ self.assertTrue(0 <= v <= 100)
+ self.assertTrue(0 <= a <= 100)
+
+ def test_cmy__all_elements_within_limits(self):
+ for c in rgba_combos_Color_generator():
+ c, m, y = c.cmy
+ self.assertTrue(0 <= c <= 1)
+ self.assertTrue(0 <= m <= 1)
+ self.assertTrue(0 <= y <= 1)
+
+ def test_i1i2i3__all_elements_within_limits(self):
+ for c in rgba_combos_Color_generator():
+ i1, i2, i3 = c.i1i2i3
+ self.assertTrue(0 <= i1 <= 1)
+ self.assertTrue(-0.5 <= i2 <= 0.5)
+ self.assertTrue(-0.5 <= i3 <= 0.5)
+
+ def test_issue_269(self):
+ """PyColor OverflowError on HSVA with hue value of 360
+
+ >>> c = pygame.Color(0)
+ >>> c.hsva = (360,0,0,0)
+ Traceback (most recent call last):
+ File "", line 1, in
+ OverflowError: this is not allowed to happen ever
+ >>> pygame.ver
+ '1.9.1release'
+ >>>
+
+ """
+
+ c = pygame.Color(0)
+ c.hsva = 360, 0, 0, 0
+ self.assertEqual(c.hsva, (0, 0, 0, 0))
+ c.hsva = 360, 100, 100, 100
+ self.assertEqual(c.hsva, (0, 100, 100, 100))
+ self.assertEqual(c, (255, 0, 0, 255))
+
+ ####################### COLORSPACE PROPERTY SANITY TESTS #######################
+
+ def colorspaces_converted_should_not_raise(self, prop):
+ fails = 0
+
+ x = 0
+ for c in rgba_combos_Color_generator():
+ x += 1
+
+ other = pygame.Color(0)
+
+ try:
+ setattr(other, prop, getattr(c, prop))
+ # eg other.hsla = c.hsla
+
+ except ValueError:
+ fails += 1
+
+ self.assertTrue(x > 0, "x is combination counter, 0 means no tests!")
+ self.assertTrue((fails, x) == (0, x))
+
+ def test_hsla__sanity_testing_converted_should_not_raise(self):
+ self.colorspaces_converted_should_not_raise("hsla")
+
+ def test_hsva__sanity_testing_converted_should_not_raise(self):
+ self.colorspaces_converted_should_not_raise("hsva")
+
+ def test_cmy__sanity_testing_converted_should_not_raise(self):
+ self.colorspaces_converted_should_not_raise("cmy")
+
+ def test_i1i2i3__sanity_testing_converted_should_not_raise(self):
+ self.colorspaces_converted_should_not_raise("i1i2i3")
+
+ ################################################################################
+
+ def colorspaces_converted_should_equate_bar_rounding(self, prop):
+ for c in rgba_combos_Color_generator():
+ other = pygame.Color(0)
+
+ try:
+ setattr(other, prop, getattr(c, prop))
+ # eg other.hsla = c.hsla
+
+ self.assertTrue(abs(other.r - c.r) <= 1)
+ self.assertTrue(abs(other.b - c.b) <= 1)
+ self.assertTrue(abs(other.g - c.g) <= 1)
+ # CMY and I1I2I3 do not care about the alpha
+ if prop not in ("cmy", "i1i2i3"):
+ self.assertTrue(abs(other.a - c.a) <= 1)
+
+ except ValueError:
+ pass # other tests will notify, this tests equation
+
+ def test_hsla__sanity_testing_converted_should_equate_bar_rounding(self):
+ self.colorspaces_converted_should_equate_bar_rounding("hsla")
+
+ def test_hsva__sanity_testing_converted_should_equate_bar_rounding(self):
+ self.colorspaces_converted_should_equate_bar_rounding("hsva")
+
+ def test_cmy__sanity_testing_converted_should_equate_bar_rounding(self):
+ self.colorspaces_converted_should_equate_bar_rounding("cmy")
+
+ def test_i1i2i3__sanity_testing_converted_should_equate_bar_rounding(self):
+ self.colorspaces_converted_should_equate_bar_rounding("i1i2i3")
+
+ ################################################################################
+
+ def test_correct_gamma__verified_against_python_implementation(self):
+ "|tags:slow|"
+ # gamma_correct defined at top of page
+
+ gammas = [i / 10.0 for i in range(1, 31)] # [0.1 ... 3.0]
+ gammas_len = len(gammas)
+
+ for i, c in enumerate(rgba_combos_Color_generator()):
+ gamma = gammas[i % gammas_len]
+
+ corrected = pygame.Color(*[gamma_correct(x, gamma) for x in tuple(c)])
+ lib_corrected = c.correct_gamma(gamma)
+
+ self.assertTrue(corrected.r == lib_corrected.r)
+ self.assertTrue(corrected.g == lib_corrected.g)
+ self.assertTrue(corrected.b == lib_corrected.b)
+ self.assertTrue(corrected.a == lib_corrected.a)
+
+ # TODO: test against statically defined verified _correct_ values
+ # assert corrected.r == 125 etc.
+
+ def test_pickle(self):
+ import pickle
+
+ c1 = pygame.Color(1, 2, 3, 4)
+ # c2 = pygame.Color(255,254,253,252)
+ pickle_string = pickle.dumps(c1)
+ c1_frompickle = pickle.loads(pickle_string)
+ self.assertEqual(c1, c1_frompickle)
+
+ ################################################################################
+ # only available if ctypes module is also available
+
+ @unittest.skipIf(IS_PYPY, "PyPy has no ctypes")
+ def test_arraystruct(self):
+ import pygame.tests.test_utils.arrinter as ai
+ import ctypes as ct
+
+ c_byte_p = ct.POINTER(ct.c_byte)
+ c = pygame.Color(5, 7, 13, 23)
+ flags = ai.PAI_CONTIGUOUS | ai.PAI_FORTRAN | ai.PAI_ALIGNED | ai.PAI_NOTSWAPPED
+ for i in range(1, 5):
+ c.set_length(i)
+ inter = ai.ArrayInterface(c)
+ self.assertEqual(inter.two, 2)
+ self.assertEqual(inter.nd, 1)
+ self.assertEqual(inter.typekind, "u")
+ self.assertEqual(inter.itemsize, 1)
+ self.assertEqual(inter.flags, flags)
+ self.assertEqual(inter.shape[0], i)
+ self.assertEqual(inter.strides[0], 1)
+ data = ct.cast(inter.data, c_byte_p)
+ for j in range(i):
+ self.assertEqual(data[j], c[j])
+
+ @unittest.skipIf(not pygame.HAVE_NEWBUF, "newbuf not implemented")
+ def test_newbuf(self):
+ from pygame.tests.test_utils import buftools
+ from ctypes import cast, POINTER, c_uint8
+
+ class ColorImporter(buftools.Importer):
+ def __init__(self, color, flags):
+ super().__init__(color, flags)
+ self.items = cast(self.buf, POINTER(c_uint8))
+
+ def __getitem__(self, index):
+ if 0 <= index < 4:
+ return self.items[index]
+ raise IndexError(f"valid index values are between 0 and 3: got {index}")
+
+ def __setitem__(self, index, value):
+ if 0 <= index < 4:
+ self.items[index] = value
+ else:
+ raise IndexError(
+ f"valid index values are between 0 and 3: got {index}"
+ )
+
+ c = pygame.Color(50, 100, 150, 200)
+ imp = ColorImporter(c, buftools.PyBUF_SIMPLE)
+ self.assertTrue(imp.obj is c)
+ self.assertEqual(imp.ndim, 0)
+ self.assertEqual(imp.itemsize, 1)
+ self.assertEqual(imp.len, 4)
+ self.assertTrue(imp.readonly)
+ self.assertTrue(imp.format is None)
+ self.assertTrue(imp.shape is None)
+ self.assertTrue(imp.strides is None)
+ self.assertTrue(imp.suboffsets is None)
+ for i in range(4):
+ self.assertEqual(c[i], imp[i])
+ imp[0] = 60
+ self.assertEqual(c.r, 60)
+ imp[1] = 110
+ self.assertEqual(c.g, 110)
+ imp[2] = 160
+ self.assertEqual(c.b, 160)
+ imp[3] = 210
+ self.assertEqual(c.a, 210)
+ imp = ColorImporter(c, buftools.PyBUF_FORMAT)
+ self.assertEqual(imp.ndim, 0)
+ self.assertEqual(imp.itemsize, 1)
+ self.assertEqual(imp.len, 4)
+ self.assertEqual(imp.format, "B")
+ self.assertEqual(imp.ndim, 0)
+ self.assertEqual(imp.itemsize, 1)
+ self.assertEqual(imp.len, 4)
+ imp = ColorImporter(c, buftools.PyBUF_ND)
+ self.assertEqual(imp.ndim, 1)
+ self.assertEqual(imp.itemsize, 1)
+ self.assertEqual(imp.len, 4)
+ self.assertTrue(imp.format is None)
+ self.assertEqual(imp.shape, (4,))
+ self.assertEqual(imp.strides, None)
+ imp = ColorImporter(c, buftools.PyBUF_STRIDES)
+ self.assertEqual(imp.ndim, 1)
+ self.assertTrue(imp.format is None)
+ self.assertEqual(imp.shape, (4,))
+ self.assertEqual(imp.strides, (1,))
+ imp = ColorImporter(c, buftools.PyBUF_C_CONTIGUOUS)
+ self.assertEqual(imp.ndim, 1)
+ imp = ColorImporter(c, buftools.PyBUF_F_CONTIGUOUS)
+ self.assertEqual(imp.ndim, 1)
+ imp = ColorImporter(c, buftools.PyBUF_ANY_CONTIGUOUS)
+ self.assertEqual(imp.ndim, 1)
+ for i in range(1, 5):
+ c.set_length(i)
+ imp = ColorImporter(c, buftools.PyBUF_ND)
+ self.assertEqual(imp.ndim, 1)
+ self.assertEqual(imp.len, i)
+ self.assertEqual(imp.shape, (i,))
+ self.assertRaises(BufferError, ColorImporter, c, buftools.PyBUF_WRITABLE)
+
+ def test_color_iter(self):
+ c = pygame.Color(50, 100, 150, 200)
+
+ # call __iter__ explicitly to test that it is defined
+ color_iterator = c.__iter__()
+ for i, val in enumerate(color_iterator):
+ self.assertEqual(c[i], val)
+
+ def test_color_contains(self):
+ c = pygame.Color(50, 60, 70)
+
+ # call __contains__ explicitly to test that it is defined
+ self.assertTrue(c.__contains__(50))
+ self.assertTrue(60 in c)
+ self.assertTrue(70 in c)
+ self.assertFalse(100 in c)
+ self.assertFalse(c.__contains__(10))
+
+ self.assertRaises(TypeError, lambda: "string" in c)
+ self.assertRaises(TypeError, lambda: 3.14159 in c)
+
+ def test_grayscale(self):
+ Color = pygame.color.Color
+
+ color = Color(255, 0, 0, 255)
+ self.assertEqual(color.grayscale(), Color(76, 76, 76, 255))
+ color = Color(3, 5, 7, 255)
+ self.assertEqual(color.grayscale(), Color(4, 4, 4, 255))
+ color = Color(3, 5, 70, 255)
+ self.assertEqual(color.grayscale(), Color(11, 11, 11, 255))
+ color = Color(3, 50, 70, 255)
+ self.assertEqual(color.grayscale(), Color(38, 38, 38, 255))
+ color = Color(30, 50, 70, 255)
+ self.assertEqual(color.grayscale(), Color(46, 46, 46, 255))
+
+ color = Color(255, 0, 0, 144)
+ self.assertEqual(color.grayscale(), Color(76, 76, 76, 144))
+ color = Color(3, 5, 7, 144)
+ self.assertEqual(color.grayscale(), Color(4, 4, 4, 144))
+ color = Color(3, 5, 70, 144)
+ self.assertEqual(color.grayscale(), Color(11, 11, 11, 144))
+ color = Color(3, 50, 70, 144)
+ self.assertEqual(color.grayscale(), Color(38, 38, 38, 144))
+ color = Color(30, 50, 70, 144)
+ self.assertEqual(color.grayscale(), Color(46, 46, 46, 144))
+
+ def test_lerp(self):
+ # setup
+ Color = pygame.color.Color
+
+ color0 = Color(0, 0, 0, 0)
+ color128 = Color(128, 128, 128, 128)
+ color255 = Color(255, 255, 255, 255)
+ color100 = Color(100, 100, 100, 100)
+
+ # type checking
+ self.assertTrue(isinstance(color0.lerp(color128, 0.5), Color))
+
+ # common value testing
+ self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64))
+ self.assertEqual(color0.lerp(color128, 0.5), Color(64, 64, 64, 64))
+ self.assertEqual(color128.lerp(color255, 0.5), Color(192, 192, 192, 192))
+ self.assertEqual(color0.lerp(color255, 0.5), Color(128, 128, 128, 128))
+
+ # testing extremes
+ self.assertEqual(color0.lerp(color100, 0), color0)
+ self.assertEqual(color0.lerp(color100, 0.01), Color(1, 1, 1, 1))
+ self.assertEqual(color0.lerp(color100, 0.99), Color(99, 99, 99, 99))
+ self.assertEqual(color0.lerp(color100, 1), color100)
+
+ # kwarg testing
+ self.assertEqual(color0.lerp(color=color100, amount=0.5), Color(50, 50, 50, 50))
+ self.assertEqual(color0.lerp(amount=0.5, color=color100), Color(50, 50, 50, 50))
+
+ # invalid input testing
+ self.assertRaises(ValueError, lambda: color0.lerp(color128, 2.5))
+ self.assertRaises(ValueError, lambda: color0.lerp(color128, -0.5))
+ self.assertRaises(ValueError, lambda: color0.lerp((256, 0, 0, 0), 0.5))
+ self.assertRaises(ValueError, lambda: color0.lerp((0, 256, 0, 0), 0.5))
+ self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 256, 0), 0.5))
+ self.assertRaises(ValueError, lambda: color0.lerp((0, 0, 0, 256), 0.5))
+ self.assertRaises(TypeError, lambda: color0.lerp(0.2, 0.5))
+
+ def test_premul_alpha(self):
+ # setup
+ Color = pygame.color.Color
+
+ color0 = Color(0, 0, 0, 0)
+ alpha0 = Color(255, 255, 255, 0)
+ alpha49 = Color(255, 0, 0, 49)
+ alpha67 = Color(0, 255, 0, 67)
+ alpha73 = Color(0, 0, 255, 73)
+ alpha128 = Color(255, 255, 255, 128)
+ alpha199 = Color(255, 255, 255, 199)
+ alpha255 = Color(128, 128, 128, 255)
+
+ # type checking
+ self.assertTrue(isinstance(color0.premul_alpha(), Color))
+
+ # hand crafted value testing
+ self.assertEqual(alpha0.premul_alpha(), Color(0, 0, 0, 0))
+ self.assertEqual(alpha49.premul_alpha(), Color(49, 0, 0, 49))
+ self.assertEqual(alpha67.premul_alpha(), Color(0, 67, 0, 67))
+ self.assertEqual(alpha73.premul_alpha(), Color(0, 0, 73, 73))
+ self.assertEqual(alpha128.premul_alpha(), Color(128, 128, 128, 128))
+ self.assertEqual(alpha199.premul_alpha(), Color(199, 199, 199, 199))
+ self.assertEqual(alpha255.premul_alpha(), Color(128, 128, 128, 255))
+
+ # full range of alpha auto sub-testing
+ test_colors = [
+ (200, 30, 74),
+ (76, 83, 24),
+ (184, 21, 6),
+ (74, 4, 74),
+ (76, 83, 24),
+ (184, 21, 234),
+ (160, 30, 74),
+ (96, 147, 204),
+ (198, 201, 60),
+ (132, 89, 74),
+ (245, 9, 224),
+ (184, 112, 6),
+ ]
+
+ for r, g, b in test_colors:
+ for a in range(255):
+ with self.subTest(r=r, g=g, b=b, a=a):
+ alpha = a / 255.0
+ self.assertEqual(
+ Color(r, g, b, a).premul_alpha(),
+ Color(
+ ((r + 1) * a) >> 8,
+ ((g + 1) * a) >> 8,
+ ((b + 1) * a) >> 8,
+ a,
+ ),
+ )
+
+ def test_update(self):
+ c = pygame.color.Color(0, 0, 0)
+ c.update(1, 2, 3, 4)
+
+ self.assertEqual(c.r, 1)
+ self.assertEqual(c.g, 2)
+ self.assertEqual(c.b, 3)
+ self.assertEqual(c.a, 4)
+
+ c = pygame.color.Color(0, 0, 0)
+ c.update([1, 2, 3, 4])
+
+ self.assertEqual(c.r, 1)
+ self.assertEqual(c.g, 2)
+ self.assertEqual(c.b, 3)
+ self.assertEqual(c.a, 4)
+
+ c = pygame.color.Color(0, 0, 0)
+ c2 = pygame.color.Color(1, 2, 3, 4)
+ c.update(c2)
+
+ self.assertEqual(c.r, 1)
+ self.assertEqual(c.g, 2)
+ self.assertEqual(c.b, 3)
+ self.assertEqual(c.a, 4)
+
+ c = pygame.color.Color(1, 1, 1)
+ c.update("black")
+
+ self.assertEqual(c.r, 0)
+ self.assertEqual(c.g, 0)
+ self.assertEqual(c.b, 0)
+ self.assertEqual(c.a, 255)
+
+ c = pygame.color.Color(0, 0, 0, 120)
+ c.set_length(3)
+ c.update(1, 2, 3)
+ self.assertEqual(len(c), 3)
+ c.set_length(4)
+ self.assertEqual(c[3], 120)
+
+ c.set_length(3)
+ c.update(1, 2, 3, 4)
+ self.assertEqual(len(c), 4)
+
+ def test_collection_abc(self):
+ c = pygame.Color(64, 70, 75, 255)
+ self.assertTrue(isinstance(c, Collection))
+ self.assertFalse(isinstance(c, Sequence))
+
+
+class SubclassTest(unittest.TestCase):
+ class MyColor(pygame.Color):
+ def __init__(self, *args, **kwds):
+ super(SubclassTest.MyColor, self).__init__(*args, **kwds)
+ self.an_attribute = True
+
+ def test_add(self):
+ mc1 = self.MyColor(128, 128, 128, 255)
+ self.assertTrue(mc1.an_attribute)
+ c2 = pygame.Color(64, 64, 64, 255)
+ mc2 = mc1 + c2
+ self.assertTrue(isinstance(mc2, self.MyColor))
+ self.assertRaises(AttributeError, getattr, mc2, "an_attribute")
+ c3 = c2 + mc1
+ self.assertTrue(type(c3) is pygame.Color)
+
+ def test_sub(self):
+ mc1 = self.MyColor(128, 128, 128, 255)
+ self.assertTrue(mc1.an_attribute)
+ c2 = pygame.Color(64, 64, 64, 255)
+ mc2 = mc1 - c2
+ self.assertTrue(isinstance(mc2, self.MyColor))
+ self.assertRaises(AttributeError, getattr, mc2, "an_attribute")
+ c3 = c2 - mc1
+ self.assertTrue(type(c3) is pygame.Color)
+
+ def test_mul(self):
+ mc1 = self.MyColor(128, 128, 128, 255)
+ self.assertTrue(mc1.an_attribute)
+ c2 = pygame.Color(64, 64, 64, 255)
+ mc2 = mc1 * c2
+ self.assertTrue(isinstance(mc2, self.MyColor))
+ self.assertRaises(AttributeError, getattr, mc2, "an_attribute")
+ c3 = c2 * mc1
+ self.assertTrue(type(c3) is pygame.Color)
+
+ def test_div(self):
+ mc1 = self.MyColor(128, 128, 128, 255)
+ self.assertTrue(mc1.an_attribute)
+ c2 = pygame.Color(64, 64, 64, 255)
+ mc2 = mc1 // c2
+ self.assertTrue(isinstance(mc2, self.MyColor))
+ self.assertRaises(AttributeError, getattr, mc2, "an_attribute")
+ c3 = c2 // mc1
+ self.assertTrue(type(c3) is pygame.Color)
+
+ def test_mod(self):
+ mc1 = self.MyColor(128, 128, 128, 255)
+ self.assertTrue(mc1.an_attribute)
+ c2 = pygame.Color(64, 64, 64, 255)
+ mc2 = mc1 % c2
+ self.assertTrue(isinstance(mc2, self.MyColor))
+ self.assertRaises(AttributeError, getattr, mc2, "an_attribute")
+ c3 = c2 % mc1
+ self.assertTrue(type(c3) is pygame.Color)
+
+ def test_inv(self):
+ mc1 = self.MyColor(64, 64, 64, 64)
+ self.assertTrue(mc1.an_attribute)
+ mc2 = ~mc1
+ self.assertTrue(isinstance(mc2, self.MyColor))
+ self.assertRaises(AttributeError, getattr, mc2, "an_attribute")
+
+ def test_correct_gamma(self):
+ mc1 = self.MyColor(64, 70, 75, 255)
+ self.assertTrue(mc1.an_attribute)
+ mc2 = mc1.correct_gamma(0.03)
+ self.assertTrue(isinstance(mc2, self.MyColor))
+ self.assertRaises(AttributeError, getattr, mc2, "an_attribute")
+
+ def test_collection_abc(self):
+ mc1 = self.MyColor(64, 70, 75, 255)
+ self.assertTrue(isinstance(mc1, Collection))
+ self.assertFalse(isinstance(mc1, Sequence))
+
+
+################################################################################
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/constants_test.py b/laplas/abstract_map/pygame/tests/constants_test.py
new file mode 100644
index 00000000..a028f982
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/constants_test.py
@@ -0,0 +1,426 @@
+import unittest
+import pygame.constants
+
+
+# K_* and KSCAN_* common names.
+K_AND_KSCAN_COMMON_NAMES = (
+ "UNKNOWN",
+ "BACKSPACE",
+ "TAB",
+ "CLEAR",
+ "RETURN",
+ "PAUSE",
+ "ESCAPE",
+ "SPACE",
+ "COMMA",
+ "MINUS",
+ "PERIOD",
+ "SLASH",
+ "0",
+ "1",
+ "2",
+ "3",
+ "4",
+ "5",
+ "6",
+ "7",
+ "8",
+ "9",
+ "SEMICOLON",
+ "EQUALS",
+ "LEFTBRACKET",
+ "BACKSLASH",
+ "RIGHTBRACKET",
+ "DELETE",
+ "KP0",
+ "KP1",
+ "KP2",
+ "KP3",
+ "KP4",
+ "KP5",
+ "KP6",
+ "KP7",
+ "KP8",
+ "KP9",
+ "KP_PERIOD",
+ "KP_DIVIDE",
+ "KP_MULTIPLY",
+ "KP_MINUS",
+ "KP_PLUS",
+ "KP_ENTER",
+ "KP_EQUALS",
+ "UP",
+ "DOWN",
+ "RIGHT",
+ "LEFT",
+ "INSERT",
+ "HOME",
+ "END",
+ "PAGEUP",
+ "PAGEDOWN",
+ "F1",
+ "F2",
+ "F3",
+ "F4",
+ "F5",
+ "F6",
+ "F7",
+ "F8",
+ "F9",
+ "F10",
+ "F11",
+ "F12",
+ "F13",
+ "F14",
+ "F15",
+ "NUMLOCK",
+ "CAPSLOCK",
+ "SCROLLOCK",
+ "RSHIFT",
+ "LSHIFT",
+ "RCTRL",
+ "LCTRL",
+ "RALT",
+ "LALT",
+ "RMETA",
+ "LMETA",
+ "LSUPER",
+ "RSUPER",
+ "MODE",
+ "HELP",
+ "PRINT",
+ "SYSREQ",
+ "BREAK",
+ "MENU",
+ "POWER",
+ "EURO",
+ "KP_0",
+ "KP_1",
+ "KP_2",
+ "KP_3",
+ "KP_4",
+ "KP_5",
+ "KP_6",
+ "KP_7",
+ "KP_8",
+ "KP_9",
+ "NUMLOCKCLEAR",
+ "SCROLLLOCK",
+ "RGUI",
+ "LGUI",
+ "PRINTSCREEN",
+ "CURRENCYUNIT",
+ "CURRENCYSUBUNIT",
+)
+
+# Constants that have the same value.
+K_AND_KSCAN_COMMON_OVERLAPS = (
+ ("KP0", "KP_0"),
+ ("KP1", "KP_1"),
+ ("KP2", "KP_2"),
+ ("KP3", "KP_3"),
+ ("KP4", "KP_4"),
+ ("KP5", "KP_5"),
+ ("KP6", "KP_6"),
+ ("KP7", "KP_7"),
+ ("KP8", "KP_8"),
+ ("KP9", "KP_9"),
+ ("NUMLOCK", "NUMLOCKCLEAR"),
+ ("SCROLLOCK", "SCROLLLOCK"),
+ ("LSUPER", "LMETA", "LGUI"),
+ ("RSUPER", "RMETA", "RGUI"),
+ ("PRINT", "PRINTSCREEN"),
+ ("BREAK", "PAUSE"),
+ ("EURO", "CURRENCYUNIT"),
+)
+
+
+def create_overlap_set(constant_names):
+ """Helper function to find overlapping constant values/names.
+
+ Returns a set of fronzensets:
+ set(frozenset(names of overlapping constants), ...)
+ """
+ # Create an overlap dict.
+ overlap_dict = {}
+
+ for name in constant_names:
+ value = getattr(pygame.constants, name)
+ overlap_dict.setdefault(value, set()).add(name)
+
+ # Get all entries with more than 1 value.
+ overlaps = set()
+
+ for overlap_names in overlap_dict.values():
+ if len(overlap_names) > 1:
+ overlaps.add(frozenset(overlap_names))
+
+ return overlaps
+
+
+class KConstantsTests(unittest.TestCase):
+ """Test K_* (key) constants."""
+
+ # K_* specific names.
+ K_SPECIFIC_NAMES = (
+ "a",
+ "b",
+ "c",
+ "d",
+ "e",
+ "f",
+ "g",
+ "h",
+ "i",
+ "j",
+ "k",
+ "l",
+ "m",
+ "n",
+ "o",
+ "p",
+ "q",
+ "r",
+ "s",
+ "t",
+ "u",
+ "v",
+ "w",
+ "x",
+ "y",
+ "z",
+ "QUOTE",
+ "BACKQUOTE",
+ "EXCLAIM",
+ "QUOTEDBL",
+ "HASH",
+ "DOLLAR",
+ "AMPERSAND",
+ "LEFTPAREN",
+ "RIGHTPAREN",
+ "ASTERISK",
+ "PLUS",
+ "COLON",
+ "LESS",
+ "GREATER",
+ "QUESTION",
+ "AT",
+ "CARET",
+ "UNDERSCORE",
+ "PERCENT",
+ )
+
+ # Create a sequence of all the K_* constant names.
+ K_NAMES = tuple("K_" + n for n in K_AND_KSCAN_COMMON_NAMES + K_SPECIFIC_NAMES)
+
+ def test_k__existence(self):
+ """Ensures K constants exist."""
+ for name in self.K_NAMES:
+ self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}")
+
+ def test_k__type(self):
+ """Ensures K constants are the correct type."""
+ for name in self.K_NAMES:
+ value = getattr(pygame.constants, name)
+
+ self.assertIs(type(value), int)
+
+ def test_k__value_overlap(self):
+ """Ensures no unexpected K constant values overlap."""
+ EXPECTED_OVERLAPS = {
+ frozenset("K_" + n for n in item) for item in K_AND_KSCAN_COMMON_OVERLAPS
+ }
+
+ overlaps = create_overlap_set(self.K_NAMES)
+
+ self.assertSetEqual(overlaps, EXPECTED_OVERLAPS)
+
+
+class KscanConstantsTests(unittest.TestCase):
+ """Test KSCAN_* (scancode) constants."""
+
+ # KSCAN_* specific names.
+ KSCAN_SPECIFIC_NAMES = (
+ "A",
+ "B",
+ "C",
+ "D",
+ "E",
+ "F",
+ "G",
+ "H",
+ "I",
+ "J",
+ "K",
+ "L",
+ "M",
+ "N",
+ "O",
+ "P",
+ "Q",
+ "R",
+ "S",
+ "T",
+ "U",
+ "V",
+ "W",
+ "X",
+ "Y",
+ "Z",
+ "APOSTROPHE",
+ "GRAVE",
+ "INTERNATIONAL1",
+ "INTERNATIONAL2",
+ "INTERNATIONAL3",
+ "INTERNATIONAL4",
+ "INTERNATIONAL5",
+ "INTERNATIONAL6",
+ "INTERNATIONAL7",
+ "INTERNATIONAL8",
+ "INTERNATIONAL9",
+ "LANG1",
+ "LANG2",
+ "LANG3",
+ "LANG4",
+ "LANG5",
+ "LANG6",
+ "LANG7",
+ "LANG8",
+ "LANG9",
+ "NONUSBACKSLASH",
+ "NONUSHASH",
+ )
+
+ # Create a sequence of all the KSCAN_* constant names.
+ KSCAN_NAMES = tuple(
+ "KSCAN_" + n for n in K_AND_KSCAN_COMMON_NAMES + KSCAN_SPECIFIC_NAMES
+ )
+
+ def test_kscan__existence(self):
+ """Ensures KSCAN constants exist."""
+ for name in self.KSCAN_NAMES:
+ self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}")
+
+ def test_kscan__type(self):
+ """Ensures KSCAN constants are the correct type."""
+ for name in self.KSCAN_NAMES:
+ value = getattr(pygame.constants, name)
+
+ self.assertIs(type(value), int)
+
+ def test_kscan__value_overlap(self):
+ """Ensures no unexpected KSCAN constant values overlap."""
+ EXPECTED_OVERLAPS = {
+ frozenset("KSCAN_" + n for n in item)
+ for item in K_AND_KSCAN_COMMON_OVERLAPS
+ }
+
+ overlaps = create_overlap_set(self.KSCAN_NAMES)
+
+ self.assertSetEqual(overlaps, EXPECTED_OVERLAPS)
+
+
+class KmodConstantsTests(unittest.TestCase):
+ """Test KMOD_* (key modifier) constants."""
+
+ # KMOD_* constant names.
+ KMOD_CONSTANTS = (
+ "KMOD_NONE",
+ "KMOD_LSHIFT",
+ "KMOD_RSHIFT",
+ "KMOD_SHIFT",
+ "KMOD_LCTRL",
+ "KMOD_RCTRL",
+ "KMOD_CTRL",
+ "KMOD_LALT",
+ "KMOD_RALT",
+ "KMOD_ALT",
+ "KMOD_LMETA",
+ "KMOD_RMETA",
+ "KMOD_META",
+ "KMOD_NUM",
+ "KMOD_CAPS",
+ "KMOD_MODE",
+ "KMOD_LGUI",
+ "KMOD_RGUI",
+ "KMOD_GUI",
+ )
+
+ def test_kmod__existence(self):
+ """Ensures KMOD constants exist."""
+ for name in self.KMOD_CONSTANTS:
+ self.assertTrue(hasattr(pygame.constants, name), f"missing constant {name}")
+
+ def test_kmod__type(self):
+ """Ensures KMOD constants are the correct type."""
+ for name in self.KMOD_CONSTANTS:
+ value = getattr(pygame.constants, name)
+
+ self.assertIs(type(value), int)
+
+ def test_kmod__value_overlap(self):
+ """Ensures no unexpected KMOD constant values overlap."""
+ # KMODs that have the same values.
+ EXPECTED_OVERLAPS = {
+ frozenset(["KMOD_LGUI", "KMOD_LMETA"]),
+ frozenset(["KMOD_RGUI", "KMOD_RMETA"]),
+ frozenset(["KMOD_GUI", "KMOD_META"]),
+ }
+
+ overlaps = create_overlap_set(self.KMOD_CONSTANTS)
+
+ self.assertSetEqual(overlaps, EXPECTED_OVERLAPS)
+
+ def test_kmod__no_bitwise_overlap(self):
+ """Ensures certain KMOD constants have no overlapping bits."""
+ NO_BITWISE_OVERLAP = (
+ "KMOD_NONE",
+ "KMOD_LSHIFT",
+ "KMOD_RSHIFT",
+ "KMOD_LCTRL",
+ "KMOD_RCTRL",
+ "KMOD_LALT",
+ "KMOD_RALT",
+ "KMOD_LMETA",
+ "KMOD_RMETA",
+ "KMOD_NUM",
+ "KMOD_CAPS",
+ "KMOD_MODE",
+ )
+
+ kmods = 0
+
+ for name in NO_BITWISE_OVERLAP:
+ value = getattr(pygame.constants, name)
+
+ self.assertFalse(kmods & value)
+
+ kmods |= value
+
+ def test_kmod__bitwise_overlap(self):
+ """Ensures certain KMOD constants have overlapping bits."""
+ # KMODS that are comprised of other KMODs.
+ KMOD_COMPRISED_DICT = {
+ "KMOD_SHIFT": ("KMOD_LSHIFT", "KMOD_RSHIFT"),
+ "KMOD_CTRL": ("KMOD_LCTRL", "KMOD_RCTRL"),
+ "KMOD_ALT": ("KMOD_LALT", "KMOD_RALT"),
+ "KMOD_META": ("KMOD_LMETA", "KMOD_RMETA"),
+ "KMOD_GUI": ("KMOD_LGUI", "KMOD_RGUI"),
+ }
+
+ for base_name, seq_names in KMOD_COMPRISED_DICT.items():
+ expected_value = 0 # Reset.
+
+ for name in seq_names:
+ expected_value |= getattr(pygame.constants, name)
+
+ value = getattr(pygame.constants, base_name)
+
+ self.assertEqual(value, expected_value)
+
+
+################################################################################
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/controller_test.py b/laplas/abstract_map/pygame/tests/controller_test.py
new file mode 100644
index 00000000..f05c00c5
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/controller_test.py
@@ -0,0 +1,357 @@
+import unittest
+import pygame
+import pygame._sdl2.controller as controller
+from pygame.tests.test_utils import prompt, question
+
+
+class ControllerModuleTest(unittest.TestCase):
+ def setUp(self):
+ controller.init()
+
+ def tearDown(self):
+ controller.quit()
+
+ def test_init(self):
+ controller.quit()
+ controller.init()
+ self.assertTrue(controller.get_init())
+
+ def test_init__multiple(self):
+ controller.init()
+ controller.init()
+ self.assertTrue(controller.get_init())
+
+ def test_quit(self):
+ controller.quit()
+ self.assertFalse(controller.get_init())
+
+ def test_quit__multiple(self):
+ controller.quit()
+ controller.quit()
+ self.assertFalse(controller.get_init())
+
+ def test_get_init(self):
+ self.assertTrue(controller.get_init())
+
+ def test_get_eventstate(self):
+ controller.set_eventstate(True)
+ self.assertTrue(controller.get_eventstate())
+
+ controller.set_eventstate(False)
+ self.assertFalse(controller.get_eventstate())
+
+ controller.set_eventstate(True)
+
+ def test_get_count(self):
+ self.assertGreaterEqual(controller.get_count(), 0)
+
+ def test_is_controller(self):
+ for i in range(controller.get_count()):
+ if controller.is_controller(i):
+ c = controller.Controller(i)
+ self.assertIsInstance(c, controller.Controller)
+ c.quit()
+ else:
+ with self.assertRaises(pygame._sdl2.sdl2.error):
+ c = controller.Controller(i)
+
+ with self.assertRaises(TypeError):
+ controller.is_controller("Test")
+
+ def test_name_forindex(self):
+ self.assertIsNone(controller.name_forindex(-1))
+
+
+class ControllerTypeTest(unittest.TestCase):
+ def setUp(self):
+ controller.init()
+
+ def tearDown(self):
+ controller.quit()
+
+ def _get_first_controller(self):
+ for i in range(controller.get_count()):
+ if controller.is_controller(i):
+ return controller.Controller(i)
+
+ def test_construction(self):
+ c = self._get_first_controller()
+ if c:
+ self.assertIsInstance(c, controller.Controller)
+ else:
+ self.skipTest("No controller connected")
+
+ def test__auto_init(self):
+ c = self._get_first_controller()
+ if c:
+ self.assertTrue(c.get_init())
+ else:
+ self.skipTest("No controller connected")
+
+ def test_get_init(self):
+ c = self._get_first_controller()
+ if c:
+ self.assertTrue(c.get_init())
+ c.quit()
+ self.assertFalse(c.get_init())
+ else:
+ self.skipTest("No controller connected")
+
+ def test_from_joystick(self):
+ for i in range(controller.get_count()):
+ if controller.is_controller(i):
+ joy = pygame.joystick.Joystick(i)
+ break
+ else:
+ self.skipTest("No controller connected")
+
+ c = controller.Controller.from_joystick(joy)
+ self.assertIsInstance(c, controller.Controller)
+
+ def test_as_joystick(self):
+ c = self._get_first_controller()
+ if c:
+ joy = c.as_joystick()
+ self.assertIsInstance(joy, type(pygame.joystick.Joystick(0)))
+ else:
+ self.skipTest("No controller connected")
+
+ def test_get_mapping(self):
+ c = self._get_first_controller()
+ if c:
+ mapping = c.get_mapping()
+ self.assertIsInstance(mapping, dict)
+ self.assertIsNotNone(mapping["a"])
+ else:
+ self.skipTest("No controller connected")
+
+ def test_set_mapping(self):
+ c = self._get_first_controller()
+ if c:
+ mapping = c.get_mapping()
+ mapping["a"] = "b3"
+ mapping["y"] = "b0"
+ c.set_mapping(mapping)
+ new_mapping = c.get_mapping()
+
+ self.assertEqual(len(mapping), len(new_mapping))
+ for i in mapping:
+ if mapping[i] not in ("a", "y"):
+ self.assertEqual(mapping[i], new_mapping[i])
+ else:
+ if i == "a":
+ self.assertEqual(new_mapping[i], mapping["y"])
+ else:
+ self.assertEqual(new_mapping[i], mapping["a"])
+ else:
+ self.skipTest("No controller connected")
+
+
+class ControllerInteractiveTest(unittest.TestCase):
+ __tags__ = ["interactive"]
+
+ def _get_first_controller(self):
+ for i in range(controller.get_count()):
+ if controller.is_controller(i):
+ return controller.Controller(i)
+
+ def setUp(self):
+ controller.init()
+
+ def tearDown(self):
+ controller.quit()
+
+ def test__get_count_interactive(self):
+ prompt(
+ "Please connect at least one controller "
+ "before the test for controller.get_count() starts"
+ )
+
+ # Reset the number of joysticks counted
+ controller.quit()
+ controller.init()
+
+ joystick_num = controller.get_count()
+ ans = question(
+ "get_count() thinks there are {} joysticks "
+ "connected. Is that correct?".format(joystick_num)
+ )
+
+ self.assertTrue(ans)
+
+ def test_set_eventstate_on_interactive(self):
+ c = self._get_first_controller()
+ if not c:
+ self.skipTest("No controller connected")
+
+ pygame.display.init()
+ pygame.font.init()
+
+ screen = pygame.display.set_mode((400, 400))
+ font = pygame.font.Font(None, 20)
+ running = True
+
+ screen.fill((255, 255, 255))
+ screen.blit(
+ font.render("Press button 'x' (on ps4) or 'a' (on xbox).", True, (0, 0, 0)),
+ (0, 0),
+ )
+ pygame.display.update()
+
+ controller.set_eventstate(True)
+
+ while running:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+
+ if event.type == pygame.CONTROLLERBUTTONDOWN:
+ running = False
+
+ pygame.display.quit()
+ pygame.font.quit()
+
+ def test_set_eventstate_off_interactive(self):
+ c = self._get_first_controller()
+ if not c:
+ self.skipTest("No controller connected")
+
+ pygame.display.init()
+ pygame.font.init()
+
+ screen = pygame.display.set_mode((400, 400))
+ font = pygame.font.Font(None, 20)
+ running = True
+
+ screen.fill((255, 255, 255))
+ screen.blit(
+ font.render("Press button 'x' (on ps4) or 'a' (on xbox).", True, (0, 0, 0)),
+ (0, 0),
+ )
+ pygame.display.update()
+
+ controller.set_eventstate(False)
+
+ while running:
+ for event in pygame.event.get(pygame.QUIT):
+ if event:
+ running = False
+
+ if c.get_button(pygame.CONTROLLER_BUTTON_A):
+ if pygame.event.peek(pygame.CONTROLLERBUTTONDOWN):
+ pygame.display.quit()
+ pygame.font.quit()
+ self.fail()
+ else:
+ running = False
+
+ pygame.display.quit()
+ pygame.font.quit()
+
+ def test_get_button_interactive(self):
+ c = self._get_first_controller()
+ if not c:
+ self.skipTest("No controller connected")
+
+ pygame.display.init()
+ pygame.font.init()
+
+ screen = pygame.display.set_mode((400, 400))
+ font = pygame.font.Font(None, 20)
+ running = True
+
+ label1 = font.render(
+ "Press button 'x' (on ps4) or 'a' (on xbox).", True, (0, 0, 0)
+ )
+
+ label2 = font.render(
+ 'The two values should match up. Press "y" or "n" to confirm.',
+ True,
+ (0, 0, 0),
+ )
+
+ is_pressed = [False, False] # event, get_button()
+ while running:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+ if event.type == pygame.CONTROLLERBUTTONDOWN and event.button == 0:
+ is_pressed[0] = True
+ if event.type == pygame.CONTROLLERBUTTONUP and event.button == 0:
+ is_pressed[0] = False
+
+ if event.type == pygame.KEYDOWN:
+ if event.key == pygame.K_y:
+ running = False
+ if event.key == pygame.K_n:
+ running = False
+ pygame.display.quit()
+ pygame.font.quit()
+ self.fail()
+
+ is_pressed[1] = c.get_button(pygame.CONTROLLER_BUTTON_A)
+
+ screen.fill((255, 255, 255))
+ screen.blit(label1, (0, 0))
+ screen.blit(label2, (0, 20))
+ screen.blit(font.render(str(is_pressed), True, (0, 0, 0)), (0, 40))
+ pygame.display.update()
+
+ pygame.display.quit()
+ pygame.font.quit()
+
+ def test_get_axis_interactive(self):
+ c = self._get_first_controller()
+ if not c:
+ self.skipTest("No controller connected")
+
+ pygame.display.init()
+ pygame.font.init()
+
+ screen = pygame.display.set_mode((400, 400))
+ font = pygame.font.Font(None, 20)
+ running = True
+
+ label1 = font.render(
+ "Press down the right trigger. The value on-screen should", True, (0, 0, 0)
+ )
+
+ label2 = font.render(
+ "indicate how far the trigger is pressed down. This value should",
+ True,
+ (0, 0, 0),
+ )
+
+ label3 = font.render(
+ 'be in the range of 0-32767. Press "y" or "n" to confirm.', True, (0, 0, 0)
+ )
+
+ while running:
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+
+ if event.type == pygame.KEYDOWN:
+ if event.key == pygame.K_y:
+ running = False
+ if event.key == pygame.K_n:
+ running = False
+ pygame.display.quit()
+ pygame.font.quit()
+ self.fail()
+
+ right_trigger = c.get_axis(pygame.CONTROLLER_AXIS_TRIGGERRIGHT)
+
+ screen.fill((255, 255, 255))
+ screen.blit(label1, (0, 0))
+ screen.blit(label2, (0, 20))
+ screen.blit(label3, (0, 40))
+ screen.blit(font.render(str(right_trigger), True, (0, 0, 0)), (0, 60))
+ pygame.display.update()
+
+ pygame.display.quit()
+ pygame.font.quit()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/cursors_test.py b/laplas/abstract_map/pygame/tests/cursors_test.py
new file mode 100644
index 00000000..8132c513
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/cursors_test.py
@@ -0,0 +1,290 @@
+import unittest
+from pygame.tests.test_utils import fixture_path
+import pygame
+
+
+class CursorsModuleTest(unittest.TestCase):
+ def test_compile(self):
+ # __doc__ (as of 2008-06-25) for pygame.cursors.compile:
+
+ # pygame.cursors.compile(strings, black, white,xor) -> data, mask
+ # compile cursor strings into cursor data
+ #
+ # This takes a set of strings with equal length and computes
+ # the binary data for that cursor. The string widths must be
+ # divisible by 8.
+ #
+ # The black and white arguments are single letter strings that
+ # tells which characters will represent black pixels, and which
+ # characters represent white pixels. All other characters are
+ # considered clear.
+ #
+ # This returns a tuple containing the cursor data and cursor mask
+ # data. Both these arguments are used when setting a cursor with
+ # pygame.mouse.set_cursor().
+
+ # Various types of input strings
+ test_cursor1 = ("X.X.XXXX", "XXXXXX..", " XXXX ")
+
+ test_cursor2 = (
+ "X.X.XXXX",
+ "XXXXXX..",
+ "XXXXXX ",
+ "XXXXXX..",
+ "XXXXXX..",
+ "XXXXXX",
+ "XXXXXX..",
+ "XXXXXX..",
+ )
+ test_cursor3 = (".XX.", " ", ".. ", "X.. X")
+
+ # Test such that total number of strings is not divisible by 8
+ with self.assertRaises(ValueError):
+ pygame.cursors.compile(test_cursor1)
+
+ # Test such that size of individual string is not divisible by 8
+ with self.assertRaises(ValueError):
+ pygame.cursors.compile(test_cursor2)
+
+ # Test such that neither size of individual string nor total number of strings is divisible by 8
+ with self.assertRaises(ValueError):
+ pygame.cursors.compile(test_cursor3)
+
+ # Test that checks whether the byte data from compile function is equal to actual byte data
+ actual_byte_data = (
+ 192,
+ 0,
+ 0,
+ 224,
+ 0,
+ 0,
+ 240,
+ 0,
+ 0,
+ 216,
+ 0,
+ 0,
+ 204,
+ 0,
+ 0,
+ 198,
+ 0,
+ 0,
+ 195,
+ 0,
+ 0,
+ 193,
+ 128,
+ 0,
+ 192,
+ 192,
+ 0,
+ 192,
+ 96,
+ 0,
+ 192,
+ 48,
+ 0,
+ 192,
+ 56,
+ 0,
+ 192,
+ 248,
+ 0,
+ 220,
+ 192,
+ 0,
+ 246,
+ 96,
+ 0,
+ 198,
+ 96,
+ 0,
+ 6,
+ 96,
+ 0,
+ 3,
+ 48,
+ 0,
+ 3,
+ 48,
+ 0,
+ 1,
+ 224,
+ 0,
+ 1,
+ 128,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ ), (
+ 192,
+ 0,
+ 0,
+ 224,
+ 0,
+ 0,
+ 240,
+ 0,
+ 0,
+ 248,
+ 0,
+ 0,
+ 252,
+ 0,
+ 0,
+ 254,
+ 0,
+ 0,
+ 255,
+ 0,
+ 0,
+ 255,
+ 128,
+ 0,
+ 255,
+ 192,
+ 0,
+ 255,
+ 224,
+ 0,
+ 255,
+ 240,
+ 0,
+ 255,
+ 248,
+ 0,
+ 255,
+ 248,
+ 0,
+ 255,
+ 192,
+ 0,
+ 247,
+ 224,
+ 0,
+ 199,
+ 224,
+ 0,
+ 7,
+ 224,
+ 0,
+ 3,
+ 240,
+ 0,
+ 3,
+ 240,
+ 0,
+ 1,
+ 224,
+ 0,
+ 1,
+ 128,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ )
+
+ cursor = pygame.cursors.compile(pygame.cursors.thickarrow_strings)
+ self.assertEqual(cursor, actual_byte_data)
+
+ # Test such that cursor byte data obtained from compile function is valid in pygame.mouse.set_cursor()
+ pygame.display.init()
+ try:
+ pygame.mouse.set_cursor((24, 24), (0, 0), *cursor)
+ except pygame.error as e:
+ if "not currently supported" in str(e):
+ unittest.skip("skipping test as set_cursor() is not supported")
+ finally:
+ pygame.display.quit()
+
+ ################################################################################
+
+ def test_load_xbm(self):
+ # __doc__ (as of 2008-06-25) for pygame.cursors.load_xbm:
+
+ # pygame.cursors.load_xbm(cursorfile, maskfile) -> cursor_args
+ # reads a pair of XBM files into set_cursor arguments
+ #
+ # Arguments can either be filenames or filelike objects
+ # with the readlines method. Not largely tested, but
+ # should work with typical XBM files.
+
+ # Test that load_xbm will take filenames as arguments
+ cursorfile = fixture_path(r"xbm_cursors/white_sizing.xbm")
+ maskfile = fixture_path(r"xbm_cursors/white_sizing_mask.xbm")
+ cursor = pygame.cursors.load_xbm(cursorfile, maskfile)
+
+ # Test that load_xbm will take file objects as arguments
+ with open(cursorfile) as cursor_f, open(maskfile) as mask_f:
+ cursor = pygame.cursors.load_xbm(cursor_f, mask_f)
+
+ # Can it load using pathlib.Path?
+ import pathlib
+
+ cursor = pygame.cursors.load_xbm(
+ pathlib.Path(cursorfile), pathlib.Path(maskfile)
+ )
+
+ # Is it in a format that mouse.set_cursor won't blow up on?
+ pygame.display.init()
+ try:
+ pygame.mouse.set_cursor(*cursor)
+ except pygame.error as e:
+ if "not currently supported" in str(e):
+ unittest.skip("skipping test as set_cursor() is not supported")
+ finally:
+ pygame.display.quit()
+
+ def test_Cursor(self):
+ """Ensure that the cursor object parses information properly"""
+
+ c1 = pygame.cursors.Cursor(pygame.SYSTEM_CURSOR_CROSSHAIR)
+
+ self.assertEqual(c1.data, (pygame.SYSTEM_CURSOR_CROSSHAIR,))
+ self.assertEqual(c1.type, "system")
+
+ c2 = pygame.cursors.Cursor(c1)
+
+ self.assertEqual(c1, c2)
+
+ with self.assertRaises(TypeError):
+ pygame.cursors.Cursor(-34002)
+ with self.assertRaises(TypeError):
+ pygame.cursors.Cursor("a", "b", "c", "d")
+ with self.assertRaises(TypeError):
+ pygame.cursors.Cursor((2,))
+
+ c3 = pygame.cursors.Cursor((0, 0), pygame.Surface((20, 20)))
+
+ self.assertEqual(c3.data[0], (0, 0))
+ self.assertEqual(c3.data[1].get_size(), (20, 20))
+ self.assertEqual(c3.type, "color")
+
+ xormask, andmask = pygame.cursors.compile(pygame.cursors.thickarrow_strings)
+ c4 = pygame.cursors.Cursor((24, 24), (0, 0), xormask, andmask)
+
+ self.assertEqual(c4.data, ((24, 24), (0, 0), xormask, andmask))
+ self.assertEqual(c4.type, "bitmap")
+
+
+################################################################################
+
+if __name__ == "__main__":
+ unittest.main()
+
+################################################################################
diff --git a/laplas/abstract_map/pygame/tests/display_test.py b/laplas/abstract_map/pygame/tests/display_test.py
new file mode 100644
index 00000000..eec521fd
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/display_test.py
@@ -0,0 +1,1199 @@
+import unittest
+import os
+import sys
+import time
+
+import pygame, pygame.transform
+
+from pygame.tests.test_utils import question
+
+from pygame import display
+
+
+class DisplayModuleTest(unittest.TestCase):
+ default_caption = "pygame window"
+
+ def setUp(self):
+ display.init()
+
+ def tearDown(self):
+ display.quit()
+
+ def test_Info(self):
+ inf = pygame.display.Info()
+ self.assertNotEqual(inf.current_h, -1)
+ self.assertNotEqual(inf.current_w, -1)
+ # probably have an older SDL than 1.2.10 if -1.
+
+ screen = pygame.display.set_mode((128, 128))
+ inf = pygame.display.Info()
+ self.assertEqual(inf.current_h, 128)
+ self.assertEqual(inf.current_w, 128)
+
+ def test_flip(self):
+ screen = pygame.display.set_mode((100, 100))
+
+ # test without a change
+ self.assertIsNone(pygame.display.flip())
+
+ # test with a change
+ pygame.Surface.fill(screen, (66, 66, 53))
+ self.assertIsNone(pygame.display.flip())
+
+ # test without display init
+ pygame.display.quit()
+ with self.assertRaises(pygame.error):
+ (pygame.display.flip())
+
+ # test without window
+ del screen
+ with self.assertRaises(pygame.error):
+ (pygame.display.flip())
+
+ def test_get_active(self):
+ """Test the get_active function"""
+
+ # Initially, the display is not active
+ pygame.display.quit()
+ self.assertEqual(pygame.display.get_active(), False)
+
+ # get_active defaults to true after a set_mode
+ pygame.display.init()
+ pygame.display.set_mode((640, 480))
+ self.assertEqual(pygame.display.get_active(), True)
+
+ # get_active after init/quit should be False
+ # since no display is visible
+ pygame.display.quit()
+ pygame.display.init()
+ self.assertEqual(pygame.display.get_active(), False)
+
+ @unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") == "dummy",
+ "requires the SDL_VIDEODRIVER to be a non dummy value",
+ )
+ def test_get_active_iconify(self):
+ """Test the get_active function after an iconify"""
+
+ # According to the docs, get_active should return
+ # false if the display is iconified
+ pygame.display.set_mode((640, 480))
+
+ pygame.event.clear()
+ pygame.display.iconify()
+
+ for _ in range(100):
+ time.sleep(0.01)
+ pygame.event.pump()
+
+ self.assertEqual(pygame.display.get_active(), False)
+
+ def test_get_caption(self):
+ screen = display.set_mode((100, 100))
+
+ self.assertEqual(display.get_caption()[0], self.default_caption)
+
+ def test_set_caption(self):
+ TEST_CAPTION = "test"
+ screen = display.set_mode((100, 100))
+
+ self.assertIsNone(display.set_caption(TEST_CAPTION))
+ self.assertEqual(display.get_caption()[0], TEST_CAPTION)
+ self.assertEqual(display.get_caption()[1], TEST_CAPTION)
+
+ def test_set_caption_kwargs(self):
+ TEST_CAPTION = "test"
+ screen = display.set_mode((100, 100))
+
+ self.assertIsNone(display.set_caption(title=TEST_CAPTION))
+ self.assertEqual(display.get_caption()[0], TEST_CAPTION)
+ self.assertEqual(display.get_caption()[1], TEST_CAPTION)
+
+ def test_caption_unicode(self):
+ TEST_CAPTION = "台"
+ display.set_caption(TEST_CAPTION)
+ self.assertEqual(display.get_caption()[0], TEST_CAPTION)
+
+ def test_get_driver(self):
+ drivers = [
+ "aalib",
+ "android",
+ "arm",
+ "cocoa",
+ "dga",
+ "directx",
+ "directfb",
+ "dummy",
+ "emscripten",
+ "fbcon",
+ "ggi",
+ "haiku",
+ "khronos",
+ "kmsdrm",
+ "nacl",
+ "offscreen",
+ "pandora",
+ "psp",
+ "qnx",
+ "raspberry",
+ "svgalib",
+ "uikit",
+ "vgl",
+ "vivante",
+ "wayland",
+ "windows",
+ "windib",
+ "winrt",
+ "x11",
+ ]
+ driver = display.get_driver()
+ self.assertIn(driver, drivers)
+
+ display.quit()
+ with self.assertRaises(pygame.error):
+ driver = display.get_driver()
+
+ def test_get_init(self):
+ """Ensures the module's initialization state can be retrieved."""
+ # display.init() already called in setUp()
+ self.assertTrue(display.get_init())
+
+ # This test can be uncommented when issues #991 and #993 are resolved.
+ @unittest.skipIf(True, "SDL2 issues")
+ def test_get_surface(self):
+ """Ensures get_surface gets the current display surface."""
+ lengths = (1, 5, 100)
+
+ for expected_size in ((w, h) for w in lengths for h in lengths):
+ for expected_depth in (8, 16, 24, 32):
+ expected_surface = display.set_mode(expected_size, 0, expected_depth)
+
+ surface = pygame.display.get_surface()
+
+ self.assertEqual(surface, expected_surface)
+ self.assertIsInstance(surface, pygame.Surface)
+ self.assertEqual(surface.get_size(), expected_size)
+ self.assertEqual(surface.get_bitsize(), expected_depth)
+
+ def test_get_surface__mode_not_set(self):
+ """Ensures get_surface handles the display mode not being set."""
+ surface = pygame.display.get_surface()
+
+ self.assertIsNone(surface)
+
+ def test_get_wm_info(self):
+ wm_info = display.get_wm_info()
+ # Assert function returns a dictionary type
+ self.assertIsInstance(wm_info, dict)
+
+ wm_info_potential_keys = {
+ "colorbuffer",
+ "connection",
+ "data",
+ "dfb",
+ "display",
+ "framebuffer",
+ "fswindow",
+ "hdc",
+ "hglrc",
+ "hinstance",
+ "lock_func",
+ "resolveFramebuffer",
+ "shell_surface",
+ "surface",
+ "taskHandle",
+ "unlock_func",
+ "wimpVersion",
+ "window",
+ "wmwindow",
+ }
+
+ # If any unexpected dict keys are present, they
+ # will be stored in set wm_info_remaining_keys
+ wm_info_remaining_keys = set(wm_info.keys()).difference(wm_info_potential_keys)
+
+ # Assert set is empty (& therefore does not
+ # contain unexpected dict keys)
+ self.assertFalse(wm_info_remaining_keys)
+
+ @unittest.skipIf(
+ (
+ "skipping for all because some failures on rasppi and maybe other platforms"
+ or os.environ.get("SDL_VIDEODRIVER") == "dummy"
+ ),
+ 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER',
+ )
+ def test_gl_get_attribute(self):
+ screen = display.set_mode((0, 0), pygame.OPENGL)
+
+ # We create a list where we store the original values of the
+ # flags before setting them with a different value.
+ original_values = []
+
+ original_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE))
+ original_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE))
+ original_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE))
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES)
+ )
+ original_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO))
+
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_ACCELERATED_VISUAL)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MAJOR_VERSION)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MINOR_VERSION)
+ )
+ original_values.append(pygame.display.gl_get_attribute(pygame.GL_CONTEXT_FLAGS))
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_CONTEXT_PROFILE_MASK)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE)
+ )
+
+ # Setting the flags with values supposedly different from the original values
+
+ # assign SDL1-supported values with gl_set_attribute
+ pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8)
+ pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 24)
+ pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, 8)
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_RED_SIZE, 16)
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_GREEN_SIZE, 16)
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_BLUE_SIZE, 16)
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_ALPHA_SIZE, 16)
+ pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, 1)
+ pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, 1)
+ pygame.display.gl_set_attribute(pygame.GL_STEREO, 0)
+ pygame.display.gl_set_attribute(pygame.GL_ACCELERATED_VISUAL, 0)
+ pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, 1)
+ pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, 1)
+ pygame.display.gl_set_attribute(pygame.GL_CONTEXT_FLAGS, 0)
+ pygame.display.gl_set_attribute(pygame.GL_CONTEXT_PROFILE_MASK, 0)
+ pygame.display.gl_set_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT, 0)
+ pygame.display.gl_set_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE, 0)
+
+ # We create a list where we store the values that we set each flag to
+ set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0]
+
+ # We create a list where we store the values after getting them
+ get_values = []
+
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCELERATED_VISUAL))
+ get_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MAJOR_VERSION)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_CONTEXT_MINOR_VERSION)
+ )
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_CONTEXT_FLAGS))
+ get_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_CONTEXT_PROFILE_MASK)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_SHARE_WITH_CURRENT_CONTEXT)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(pygame.GL_FRAMEBUFFER_SRGB_CAPABLE)
+ )
+
+ # We check to see if the values that we get correspond to the values that we set
+ # them to or to the original values.
+ for i in range(len(original_values)):
+ self.assertTrue(
+ (get_values[i] == original_values[i])
+ or (get_values[i] == set_values[i])
+ )
+
+ # test using non-flag argument
+ with self.assertRaises(TypeError):
+ pygame.display.gl_get_attribute("DUMMY")
+
+ @unittest.skipIf(
+ (
+ "skipping for all because some failures on rasppi and maybe other platforms"
+ or os.environ.get("SDL_VIDEODRIVER") == "dummy"
+ ),
+ 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER',
+ )
+ def test_gl_get_attribute_kwargs(self):
+ screen = display.set_mode((0, 0), pygame.OPENGL)
+
+ # We create a list where we store the original values of the
+ # flags before setting them with a different value.
+ original_values = []
+
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES)
+ )
+ original_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO))
+
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCELERATED_VISUAL)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_FLAGS)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT)
+ )
+ original_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE)
+ )
+
+ # Setting the flags with values supposedly different from the original values
+
+ # assign SDL1-supported values with gl_set_attribute
+ pygame.display.gl_set_attribute(flag=pygame.GL_ALPHA_SIZE, value=8)
+ pygame.display.gl_set_attribute(flag=pygame.GL_DEPTH_SIZE, value=24)
+ pygame.display.gl_set_attribute(flag=pygame.GL_STENCIL_SIZE, value=8)
+ pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_RED_SIZE, value=16)
+ pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE, value=16)
+ pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE, value=16)
+ pygame.display.gl_set_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE, value=16)
+ pygame.display.gl_set_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS, value=1)
+ pygame.display.gl_set_attribute(flag=pygame.GL_MULTISAMPLESAMPLES, value=1)
+ pygame.display.gl_set_attribute(flag=pygame.GL_STEREO, value=0)
+ pygame.display.gl_set_attribute(flag=pygame.GL_ACCELERATED_VISUAL, value=0)
+ pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION, value=1)
+ pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION, value=1)
+ pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_FLAGS, value=0)
+ pygame.display.gl_set_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK, value=0)
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT, value=0
+ )
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE, value=0
+ )
+
+ # We create a list where we store the values that we set each flag to
+ set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0]
+
+ # We create a list where we store the values after getting them
+ get_values = []
+
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE))
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES)
+ )
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO))
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCELERATED_VISUAL)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MAJOR_VERSION)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_MINOR_VERSION)
+ )
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_FLAGS))
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_CONTEXT_PROFILE_MASK)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_SHARE_WITH_CURRENT_CONTEXT)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_FRAMEBUFFER_SRGB_CAPABLE)
+ )
+
+ # We check to see if the values that we get correspond to the values that we set
+ # them to or to the original values.
+ for i in range(len(original_values)):
+ self.assertTrue(
+ (get_values[i] == original_values[i])
+ or (get_values[i] == set_values[i])
+ )
+
+ # test using non-flag argument
+ with self.assertRaises(TypeError):
+ pygame.display.gl_get_attribute("DUMMY")
+
+ @unittest.skipIf(
+ (
+ "skipping for all because some failures on rasppi and maybe other platforms"
+ or os.environ.get("SDL_VIDEODRIVER") == "dummy"
+ ),
+ 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER',
+ )
+ def test_gl_set_attribute(self):
+ # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute:
+
+ # pygame.display.gl_set_attribute(flag, value): return None
+ # request an opengl display attribute for the display mode
+ #
+ # When calling pygame.display.set_mode() with the pygame.OPENGL flag,
+ # Pygame automatically handles setting the OpenGL attributes like
+ # color and doublebuffering. OpenGL offers several other attributes
+ # you may want control over. Pass one of these attributes as the flag,
+ # and its appropriate value. This must be called before
+ # pygame.display.set_mode()
+ #
+ # The OPENGL flags are;
+ # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE,
+ # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE,
+ # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO
+
+ screen = display.set_mode((0, 0), pygame.OPENGL)
+
+ # We create a list where we store the values that we set each flag to
+ set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0]
+
+ # Setting the flags with values supposedly different from the original values
+
+ # assign SDL1-supported values with gl_set_attribute
+ pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, set_values[0])
+ pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, set_values[1])
+ pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, set_values[2])
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_RED_SIZE, set_values[3])
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_GREEN_SIZE, set_values[4])
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_BLUE_SIZE, set_values[5])
+ pygame.display.gl_set_attribute(pygame.GL_ACCUM_ALPHA_SIZE, set_values[6])
+ pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, set_values[7])
+ pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, set_values[8])
+ pygame.display.gl_set_attribute(pygame.GL_STEREO, set_values[9])
+
+ # We create a list where we store the values after getting them
+ get_values = []
+
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_RED_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_GREEN_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_BLUE_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_ACCUM_ALPHA_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLEBUFFERS))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES))
+ get_values.append(pygame.display.gl_get_attribute(pygame.GL_STEREO))
+
+ # We check to see if the values that we get correspond to the values that we set
+ # them to or to the original values.
+ for i in range(len(set_values)):
+ self.assertTrue(get_values[i] == set_values[i])
+
+ # test using non-flag argument
+ with self.assertRaises(TypeError):
+ pygame.display.gl_get_attribute("DUMMY")
+
+ @unittest.skipIf(
+ (
+ "skipping for all because some failures on rasppi and maybe other platforms"
+ or os.environ.get("SDL_VIDEODRIVER") == "dummy"
+ ),
+ 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER',
+ )
+ def test_gl_set_attribute_kwargs(self):
+ # __doc__ (as of 2008-08-02) for pygame.display.gl_set_attribute:
+
+ # pygame.display.gl_set_attribute(flag, value): return None
+ # request an opengl display attribute for the display mode
+ #
+ # When calling pygame.display.set_mode() with the pygame.OPENGL flag,
+ # Pygame automatically handles setting the OpenGL attributes like
+ # color and doublebuffering. OpenGL offers several other attributes
+ # you may want control over. Pass one of these attributes as the flag,
+ # and its appropriate value. This must be called before
+ # pygame.display.set_mode()
+ #
+ # The OPENGL flags are;
+ # GL_ALPHA_SIZE, GL_DEPTH_SIZE, GL_STENCIL_SIZE, GL_ACCUM_RED_SIZE,
+ # GL_ACCUM_GREEN_SIZE, GL_ACCUM_BLUE_SIZE, GL_ACCUM_ALPHA_SIZE,
+ # GL_MULTISAMPLEBUFFERS, GL_MULTISAMPLESAMPLES, GL_STEREO
+
+ screen = display.set_mode((0, 0), pygame.OPENGL)
+
+ # We create a list where we store the values that we set each flag to
+ set_values = [8, 24, 8, 16, 16, 16, 16, 1, 1, 0]
+
+ # Setting the flags with values supposedly different from the original values
+
+ # assign SDL1-supported values with gl_set_attribute
+ pygame.display.gl_set_attribute(flag=pygame.GL_ALPHA_SIZE, value=set_values[0])
+ pygame.display.gl_set_attribute(flag=pygame.GL_DEPTH_SIZE, value=set_values[1])
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_STENCIL_SIZE, value=set_values[2]
+ )
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_ACCUM_RED_SIZE, value=set_values[3]
+ )
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_ACCUM_GREEN_SIZE, value=set_values[4]
+ )
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_ACCUM_BLUE_SIZE, value=set_values[5]
+ )
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_ACCUM_ALPHA_SIZE, value=set_values[6]
+ )
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_MULTISAMPLEBUFFERS, value=set_values[7]
+ )
+ pygame.display.gl_set_attribute(
+ flag=pygame.GL_MULTISAMPLESAMPLES, value=set_values[8]
+ )
+ pygame.display.gl_set_attribute(flag=pygame.GL_STEREO, value=set_values[9])
+
+ # We create a list where we store the values after getting them
+ get_values = []
+
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_ALPHA_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_DEPTH_SIZE))
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STENCIL_SIZE))
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_RED_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_GREEN_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_BLUE_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_ACCUM_ALPHA_SIZE)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLEBUFFERS)
+ )
+ get_values.append(
+ pygame.display.gl_get_attribute(flag=pygame.GL_MULTISAMPLESAMPLES)
+ )
+ get_values.append(pygame.display.gl_get_attribute(flag=pygame.GL_STEREO))
+
+ # We check to see if the values that we get correspond to the values that we set
+ # them to or to the original values.
+ for i in range(len(set_values)):
+ self.assertTrue(get_values[i] == set_values[i])
+
+ # test using non-flag argument
+ with self.assertRaises(TypeError):
+ pygame.display.gl_get_attribute("DUMMY")
+
+ @unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") in ["dummy", "android"],
+ "iconify is only supported on some video drivers/platforms",
+ )
+ def test_iconify(self):
+ pygame.display.set_mode((640, 480))
+
+ self.assertEqual(pygame.display.get_active(), True)
+
+ success = pygame.display.iconify()
+
+ if success:
+ active_event = window_minimized_event = False
+ # make sure we cycle the event loop enough to get the display
+ # hidden. Test that both ACTIVEEVENT and WINDOWMINIMISED event appears
+ for _ in range(50):
+ time.sleep(0.01)
+ for event in pygame.event.get():
+ if event.type == pygame.ACTIVEEVENT:
+ if not event.gain and event.state == pygame.APPACTIVE:
+ active_event = True
+ if event.type == pygame.WINDOWMINIMIZED:
+ window_minimized_event = True
+
+ self.assertTrue(window_minimized_event)
+ self.assertTrue(active_event)
+ self.assertFalse(pygame.display.get_active())
+
+ else:
+ self.fail("Iconify not supported on this platform, please skip")
+
+ def test_init(self):
+ """Ensures the module is initialized after init called."""
+ # display.init() already called in setUp(), so quit and re-init
+ display.quit()
+ display.init()
+
+ self.assertTrue(display.get_init())
+
+ def test_init__multiple(self):
+ """Ensures the module is initialized after multiple init calls."""
+ display.init()
+ display.init()
+
+ self.assertTrue(display.get_init())
+
+ def test_list_modes(self):
+ modes = pygame.display.list_modes(depth=0, flags=pygame.FULLSCREEN, display=0)
+ # modes == -1 means any mode is supported.
+ if modes != -1:
+ self.assertEqual(len(modes[0]), 2)
+ self.assertEqual(type(modes[0][0]), int)
+
+ modes = pygame.display.list_modes()
+ if modes != -1:
+ self.assertEqual(len(modes[0]), 2)
+ self.assertEqual(type(modes[0][0]), int)
+ self.assertEqual(len(modes), len(set(modes)))
+
+ modes = pygame.display.list_modes(depth=0, flags=0, display=0)
+ if modes != -1:
+ self.assertEqual(len(modes[0]), 2)
+ self.assertEqual(type(modes[0][0]), int)
+
+ def test_mode_ok(self):
+ pygame.display.mode_ok((128, 128))
+ modes = pygame.display.list_modes()
+ if modes != -1:
+ size = modes[0]
+ self.assertNotEqual(pygame.display.mode_ok(size), 0)
+
+ pygame.display.mode_ok((128, 128), 0, 32)
+ pygame.display.mode_ok((128, 128), flags=0, depth=32, display=0)
+
+ def test_mode_ok_fullscreen(self):
+ modes = pygame.display.list_modes()
+ if modes != -1:
+ size = modes[0]
+ self.assertNotEqual(
+ pygame.display.mode_ok(size, flags=pygame.FULLSCREEN), 0
+ )
+
+ def test_mode_ok_scaled(self):
+ modes = pygame.display.list_modes()
+ if modes != -1:
+ size = modes[0]
+ self.assertNotEqual(pygame.display.mode_ok(size, flags=pygame.SCALED), 0)
+
+ def test_get_num_displays(self):
+ self.assertGreater(pygame.display.get_num_displays(), 0)
+
+ def test_quit(self):
+ """Ensures the module is not initialized after quit called."""
+ display.quit()
+
+ self.assertFalse(display.get_init())
+
+ def test_quit__multiple(self):
+ """Ensures the module is not initialized after multiple quit calls."""
+ display.quit()
+ display.quit()
+
+ self.assertFalse(display.get_init())
+
+ @unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") == "dummy", "Needs a not dummy videodriver"
+ )
+ def test_set_gamma(self):
+ pygame.display.set_mode((1, 1))
+
+ gammas = [0.25, 0.5, 0.88, 1.0]
+ for gamma in gammas:
+ with self.subTest(gamma=gamma):
+ with self.assertWarns(DeprecationWarning):
+ self.assertEqual(pygame.display.set_gamma(gamma), True)
+ self.assertEqual(pygame.display.set_gamma(gamma), True)
+
+ @unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") == "dummy", "Needs a not dummy videodriver"
+ )
+ def test_set_gamma__tuple(self):
+ pygame.display.set_mode((1, 1))
+
+ gammas = [(0.5, 0.5, 0.5), (1.0, 1.0, 1.0), (0.25, 0.33, 0.44)]
+ for r, g, b in gammas:
+ with self.subTest(r=r, g=g, b=b):
+ self.assertEqual(pygame.display.set_gamma(r, g, b), True)
+
+ @unittest.skipIf(
+ not hasattr(pygame.display, "set_gamma_ramp"),
+ "Not all systems and hardware support gamma ramps",
+ )
+ def test_set_gamma_ramp(self):
+ # __doc__ (as of 2008-08-02) for pygame.display.set_gamma_ramp:
+
+ # change the hardware gamma ramps with a custom lookup
+ # pygame.display.set_gamma_ramp(red, green, blue): return bool
+ # set_gamma_ramp(red, green, blue): return bool
+ #
+ # Set the red, green, and blue gamma ramps with an explicit lookup
+ # table. Each argument should be sequence of 256 integers. The
+ # integers should range between 0 and 0xffff. Not all systems and
+ # hardware support gamma ramps, if the function succeeds it will
+ # return True.
+ #
+ pygame.display.set_mode((5, 5))
+ r = list(range(256))
+ g = [number + 256 for number in r]
+ b = [number + 256 for number in g]
+ with self.assertWarns(DeprecationWarning):
+ isSupported = pygame.display.set_gamma_ramp(r, g, b)
+ if isSupported:
+ self.assertTrue(pygame.display.set_gamma_ramp(r, g, b))
+ else:
+ self.assertFalse(pygame.display.set_gamma_ramp(r, g, b))
+
+ def test_set_mode_kwargs(self):
+ pygame.display.set_mode(size=(1, 1), flags=0, depth=0, display=0)
+
+ def test_set_mode_scaled(self):
+ surf = pygame.display.set_mode(
+ size=(1, 1), flags=pygame.SCALED, depth=0, display=0
+ )
+ winsize = pygame.display.get_window_size()
+ self.assertEqual(
+ winsize[0] % surf.get_size()[0],
+ 0,
+ "window width should be a multiple of the surface width",
+ )
+ self.assertEqual(
+ winsize[1] % surf.get_size()[1],
+ 0,
+ "window height should be a multiple of the surface height",
+ )
+ self.assertEqual(
+ winsize[0] / surf.get_size()[0], winsize[1] / surf.get_size()[1]
+ )
+
+ def test_set_mode_vector2(self):
+ pygame.display.set_mode(pygame.Vector2(1, 1))
+
+ def test_set_mode_unscaled(self):
+ """Ensures a window created with SCALED can become smaller."""
+ # see https://github.com/pygame/pygame/issues/2327
+
+ screen = pygame.display.set_mode((300, 300), pygame.SCALED)
+ self.assertEqual(screen.get_size(), (300, 300))
+
+ screen = pygame.display.set_mode((200, 200))
+ self.assertEqual(screen.get_size(), (200, 200))
+
+ def test_screensaver_support(self):
+ pygame.display.set_allow_screensaver(True)
+ self.assertTrue(pygame.display.get_allow_screensaver())
+ pygame.display.set_allow_screensaver(False)
+ self.assertFalse(pygame.display.get_allow_screensaver())
+ pygame.display.set_allow_screensaver()
+ self.assertTrue(pygame.display.get_allow_screensaver())
+
+ # the following test fails always with SDL2
+ @unittest.skipIf(True, "set_palette() not supported in SDL2")
+ def test_set_palette(self):
+ with self.assertRaises(pygame.error):
+ palette = [1, 2, 3]
+ pygame.display.set_palette(palette)
+ pygame.display.set_mode((1024, 768), 0, 8)
+ palette = []
+ self.assertIsNone(pygame.display.set_palette(palette))
+
+ with self.assertRaises(ValueError):
+ palette = 12
+ pygame.display.set_palette(palette)
+ with self.assertRaises(TypeError):
+ palette = [[1, 2], [1, 2]]
+ pygame.display.set_palette(palette)
+ with self.assertRaises(TypeError):
+ palette = [[0, 0, 0, 0, 0]] + [[x, x, x, x, x] for x in range(1, 255)]
+ pygame.display.set_palette(palette)
+ with self.assertRaises(TypeError):
+ palette = "qwerty"
+ pygame.display.set_palette(palette)
+ with self.assertRaises(TypeError):
+ palette = [[123, 123, 123] * 10000]
+ pygame.display.set_palette(palette)
+ with self.assertRaises(TypeError):
+ palette = [1, 2, 3]
+ pygame.display.set_palette(palette)
+
+ skip_list = ["dummy", "android"]
+
+ @unittest.skipIf(True, "set_palette() not supported in SDL2")
+ def test_set_palette_kwargs(self):
+ with self.assertRaises(pygame.error):
+ palette = [1, 2, 3]
+ pygame.display.set_palette(palette=palette)
+ pygame.display.set_mode((1024, 768), 0, 8)
+ palette = []
+ self.assertIsNone(pygame.display.set_palette(palette=palette))
+
+ with self.assertRaises(ValueError):
+ palette = 12
+ pygame.display.set_palette(palette=palette)
+ with self.assertRaises(TypeError):
+ palette = [[1, 2], [1, 2]]
+ pygame.display.set_palette(palette=palette)
+ with self.assertRaises(TypeError):
+ palette = [[0, 0, 0, 0, 0]] + [[x, x, x, x, x] for x in range(1, 255)]
+ pygame.display.set_palette(palette=palette)
+ with self.assertRaises(TypeError):
+ palette = "qwerty"
+ pygame.display.set_palette(palette=palette)
+ with self.assertRaises(TypeError):
+ palette = [[123, 123, 123] * 10000]
+ pygame.display.set_palette(palette=palette)
+ with self.assertRaises(TypeError):
+ palette = [1, 2, 3]
+ pygame.display.set_palette(palette=palette)
+
+ skip_list = ["dummy", "android"]
+
+ @unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") in skip_list,
+ "requires the SDL_VIDEODRIVER to be non dummy",
+ )
+ def test_toggle_fullscreen(self):
+ """Test for toggle fullscreen"""
+
+ # try to toggle fullscreen with no active display
+ # this should result in an error
+ pygame.display.quit()
+ with self.assertRaises(pygame.error):
+ pygame.display.toggle_fullscreen()
+
+ pygame.display.init()
+ width_height = (640, 480)
+ test_surf = pygame.display.set_mode(width_height)
+
+ # try to toggle fullscreen
+ try:
+ pygame.display.toggle_fullscreen()
+
+ except pygame.error:
+ self.fail()
+
+ else:
+ # if toggle success, the width/height should be a
+ # value found in list_modes
+ if pygame.display.toggle_fullscreen() == 1:
+ boolean = (
+ test_surf.get_width(),
+ test_surf.get_height(),
+ ) in pygame.display.list_modes(
+ depth=0, flags=pygame.FULLSCREEN, display=0
+ )
+
+ self.assertEqual(boolean, True)
+
+ # if not original width/height should be preserved
+ else:
+ self.assertEqual(
+ (test_surf.get_width(), test_surf.get_height()), width_height
+ )
+
+
+class DisplayUpdateTest(unittest.TestCase):
+ def question(self, qstr):
+ """this is used in the interactive subclass."""
+
+ def setUp(self):
+ display.init()
+ self.screen = pygame.display.set_mode((500, 500))
+ self.screen.fill("black")
+ pygame.display.flip()
+ pygame.event.pump() # so mac updates
+
+ def tearDown(self):
+ display.quit()
+
+ def test_update_negative(self):
+ """takes rects with negative values."""
+ self.screen.fill("green")
+
+ r1 = pygame.Rect(0, 0, 100, 100)
+ pygame.display.update(r1)
+
+ r2 = pygame.Rect(-10, 0, 100, 100)
+ pygame.display.update(r2)
+
+ r3 = pygame.Rect(-10, 0, -100, -100)
+ pygame.display.update(r3)
+
+ self.question("Is the screen green in (0, 0, 100, 100)?")
+
+ def test_update_sequence(self):
+ """only updates the part of the display given by the rects."""
+ self.screen.fill("green")
+ rects = [
+ pygame.Rect(0, 0, 100, 100),
+ pygame.Rect(100, 0, 100, 100),
+ pygame.Rect(200, 0, 100, 100),
+ pygame.Rect(300, 300, 100, 100),
+ ]
+ pygame.display.update(rects)
+ pygame.event.pump() # so mac updates
+
+ self.question(f"Is the screen green in {rects}?")
+
+ def test_update_none_skipped(self):
+ """None is skipped inside sequences."""
+ self.screen.fill("green")
+ rects = (
+ None,
+ pygame.Rect(100, 0, 100, 100),
+ None,
+ pygame.Rect(200, 0, 100, 100),
+ pygame.Rect(300, 300, 100, 100),
+ )
+ pygame.display.update(rects)
+ pygame.event.pump() # so mac updates
+
+ self.question(f"Is the screen green in {rects}?")
+
+ def test_update_none(self):
+ """does NOT update the display."""
+ self.screen.fill("green")
+ pygame.display.update(None)
+ pygame.event.pump() # so mac updates
+ self.question("Is the screen black and NOT green?")
+
+ def test_update_no_args(self):
+ """does NOT update the display."""
+ self.screen.fill("green")
+ pygame.display.update()
+ pygame.event.pump() # so mac updates
+ self.question("Is the WHOLE screen green?")
+
+ def test_update_args(self):
+ """updates the display using the args as a rect."""
+ self.screen.fill("green")
+ pygame.display.update(100, 100, 100, 100)
+ pygame.event.pump() # so mac updates
+ self.question("Is the screen green in (100, 100, 100, 100)?")
+
+ def test_update_incorrect_args(self):
+ """raises a ValueError when inputs are wrong."""
+
+ with self.assertRaises(ValueError):
+ pygame.display.update(100, "asdf", 100, 100)
+
+ with self.assertRaises(ValueError):
+ pygame.display.update([100, "asdf", 100, 100])
+
+ def test_update_no_init(self):
+ """raises a pygame.error."""
+
+ pygame.display.quit()
+ with self.assertRaises(pygame.error):
+ pygame.display.update()
+
+
+class DisplayUpdateInteractiveTest(DisplayUpdateTest):
+ """Because we want these tests to run as interactive and not interactive."""
+
+ __tags__ = ["interactive"]
+
+ def question(self, qstr):
+ """since this is the interactive subclass we ask a question."""
+ question(qstr)
+
+
+class DisplayInteractiveTest(unittest.TestCase):
+ __tags__ = ["interactive"]
+
+ def test_set_icon_interactive(self):
+ os.environ["SDL_VIDEO_WINDOW_POS"] = "100,250"
+ pygame.display.quit()
+ pygame.display.init()
+
+ test_icon = pygame.Surface((32, 32))
+ test_icon.fill((255, 0, 0))
+
+ pygame.display.set_icon(test_icon)
+ screen = pygame.display.set_mode((400, 100))
+ pygame.display.set_caption("Is the window icon a red square?")
+
+ response = question("Is the display icon red square?")
+
+ self.assertTrue(response)
+ pygame.display.quit()
+
+ def test_set_gamma_ramp(self):
+ os.environ["SDL_VIDEO_WINDOW_POS"] = "100,250"
+ pygame.display.quit()
+ pygame.display.init()
+
+ screen = pygame.display.set_mode((400, 100))
+ screen.fill((100, 100, 100))
+
+ blue_ramp = [x * 256 for x in range(0, 256)]
+ blue_ramp[100] = 150 * 256 # Can't tint too far or gamma ramps fail
+ normal_ramp = [x * 256 for x in range(0, 256)]
+ # test to see if this platform supports gamma ramps
+ gamma_success = False
+ if pygame.display.set_gamma_ramp(normal_ramp, normal_ramp, blue_ramp):
+ pygame.display.update()
+ gamma_success = True
+
+ if gamma_success:
+ response = question("Is the window background tinted blue?")
+ self.assertTrue(response)
+ # restore normal ramp
+ pygame.display.set_gamma_ramp(normal_ramp, normal_ramp, normal_ramp)
+
+ pygame.display.quit()
+
+
+class FullscreenToggleTests(unittest.TestCase):
+ __tags__ = ["interactive"]
+
+ screen = None
+ font = None
+ isfullscreen = False
+
+ WIDTH = 800
+ HEIGHT = 600
+
+ def setUp(self):
+ pygame.init()
+ if sys.platform == "win32":
+ # known issue with windows, must have mode from pygame.display.list_modes()
+ # or window created with flag pygame.SCALED
+ self.screen = pygame.display.set_mode(
+ (self.WIDTH, self.HEIGHT), flags=pygame.SCALED
+ )
+ else:
+ self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT))
+ pygame.display.set_caption("Fullscreen Tests")
+ self.screen.fill((255, 255, 255))
+ pygame.display.flip()
+ self.font = pygame.font.Font(None, 32)
+
+ def tearDown(self):
+ if self.isfullscreen:
+ pygame.display.toggle_fullscreen()
+ pygame.quit()
+
+ def visual_test(self, fullscreen=False):
+ text = ""
+ if fullscreen:
+ if not self.isfullscreen:
+ pygame.display.toggle_fullscreen()
+ self.isfullscreen = True
+ text = "Is this in fullscreen? [y/n]"
+ else:
+ if self.isfullscreen:
+ pygame.display.toggle_fullscreen()
+ self.isfullscreen = False
+ text = "Is this not in fullscreen [y/n]"
+ s = self.font.render(text, False, (0, 0, 0))
+ self.screen.blit(s, (self.WIDTH / 2 - self.font.size(text)[0] / 2, 100))
+ pygame.display.flip()
+
+ while True:
+ for event in pygame.event.get():
+ if event.type == pygame.KEYDOWN:
+ if event.key == pygame.K_ESCAPE:
+ return False
+ if event.key == pygame.K_y:
+ return True
+ if event.key == pygame.K_n:
+ return False
+ if event.type == pygame.QUIT:
+ return False
+
+ def test_fullscreen_true(self):
+ self.assertTrue(self.visual_test(fullscreen=True))
+
+ def test_fullscreen_false(self):
+ self.assertTrue(self.visual_test(fullscreen=False))
+
+
+@unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") == "dummy",
+ 'OpenGL requires a non-"dummy" SDL_VIDEODRIVER',
+)
+class DisplayOpenGLTest(unittest.TestCase):
+ def test_screen_size_opengl(self):
+ """returns a surface with the same size requested.
+ |tags:display,slow,opengl|
+ """
+ pygame.display.init()
+ screen = pygame.display.set_mode((640, 480), pygame.OPENGL)
+ self.assertEqual((640, 480), screen.get_size())
+
+
+class X11CrashTest(unittest.TestCase):
+ def test_x11_set_mode_crash_gh1654(self):
+ # Test for https://github.com/pygame/pygame/issues/1654
+ # If unfixed, this will trip a segmentation fault
+ pygame.display.init()
+ pygame.display.quit()
+ screen = pygame.display.set_mode((640, 480), 0)
+ self.assertEqual((640, 480), screen.get_size())
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/docs_test.py b/laplas/abstract_map/pygame/tests/docs_test.py
new file mode 100644
index 00000000..de021a8e
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/docs_test.py
@@ -0,0 +1,35 @@
+import os
+import subprocess
+import sys
+import unittest
+
+
+class DocsIncludedTest(unittest.TestCase):
+ def test_doc_import_works(self):
+ from pygame.docs.__main__ import has_local_docs, open_docs
+
+ @unittest.skipIf("CI" not in os.environ, "Docs not required for local builds")
+ def test_docs_included(self):
+ from pygame.docs.__main__ import has_local_docs
+
+ self.assertTrue(has_local_docs())
+
+ @unittest.skipIf("CI" not in os.environ, "Docs not required for local builds")
+ def test_docs_command(self):
+ try:
+ subprocess.run(
+ [sys.executable, "-m", "pygame.docs"],
+ timeout=5,
+ # check ensures an exception is raised when the process fails
+ check=True,
+ # pipe stdout/stderr so that they don't clutter main stdout
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ except subprocess.TimeoutExpired:
+ # timeout errors are not an issue
+ pass
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/draw_test.py b/laplas/abstract_map/pygame/tests/draw_test.py
new file mode 100644
index 00000000..a33f8649
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/draw_test.py
@@ -0,0 +1,6577 @@
+import math
+import unittest
+import sys
+import warnings
+
+import pygame
+from pygame import draw
+from pygame import draw_py
+from pygame.locals import SRCALPHA
+from pygame.tests import test_utils
+from pygame.math import Vector2
+
+
+RED = BG_RED = pygame.Color("red")
+GREEN = FG_GREEN = pygame.Color("green")
+
+# Clockwise from the top left corner and ending with the center point.
+RECT_POSITION_ATTRIBUTES = (
+ "topleft",
+ "midtop",
+ "topright",
+ "midright",
+ "bottomright",
+ "midbottom",
+ "bottomleft",
+ "midleft",
+ "center",
+)
+
+
+def get_border_values(surface, width, height):
+ """Returns a list containing lists with the values of the surface's
+ borders.
+ """
+ border_top = [surface.get_at((x, 0)) for x in range(width)]
+ border_left = [surface.get_at((0, y)) for y in range(height)]
+ border_right = [surface.get_at((width - 1, y)) for y in range(height)]
+ border_bottom = [surface.get_at((x, height - 1)) for x in range(width)]
+
+ return [border_top, border_left, border_right, border_bottom]
+
+
+def corners(surface):
+ """Returns a tuple with the corner positions of the given surface.
+
+ Clockwise from the top left corner.
+ """
+ width, height = surface.get_size()
+ return ((0, 0), (width - 1, 0), (width - 1, height - 1), (0, height - 1))
+
+
+def rect_corners_mids_and_center(rect):
+ """Returns a tuple with each corner, mid, and the center for a given rect.
+
+ Clockwise from the top left corner and ending with the center point.
+ """
+ return (
+ rect.topleft,
+ rect.midtop,
+ rect.topright,
+ rect.midright,
+ rect.bottomright,
+ rect.midbottom,
+ rect.bottomleft,
+ rect.midleft,
+ rect.center,
+ )
+
+
+def border_pos_and_color(surface):
+ """Yields each border position and its color for a given surface.
+
+ Clockwise from the top left corner.
+ """
+ width, height = surface.get_size()
+ right, bottom = width - 1, height - 1
+
+ # Top edge.
+ for x in range(width):
+ pos = (x, 0)
+ yield pos, surface.get_at(pos)
+
+ # Right edge.
+ # Top right done in top edge loop.
+ for y in range(1, height):
+ pos = (right, y)
+ yield pos, surface.get_at(pos)
+
+ # Bottom edge.
+ # Bottom right done in right edge loop.
+ for x in range(right - 1, -1, -1):
+ pos = (x, bottom)
+ yield pos, surface.get_at(pos)
+
+ # Left edge.
+ # Bottom left done in bottom edge loop. Top left done in top edge loop.
+ for y in range(bottom - 1, 0, -1):
+ pos = (0, y)
+ yield pos, surface.get_at(pos)
+
+
+def get_color_points(surface, color, bounds_rect=None, match_color=True):
+ """Get all the points of a given color on the surface within the given
+ bounds.
+
+ If bounds_rect is None the full surface is checked.
+ If match_color is True, all points matching the color are returned,
+ otherwise all points not matching the color are returned.
+ """
+ get_at = surface.get_at # For possible speed up.
+
+ if bounds_rect is None:
+ x_range = range(surface.get_width())
+ y_range = range(surface.get_height())
+ else:
+ x_range = range(bounds_rect.left, bounds_rect.right)
+ y_range = range(bounds_rect.top, bounds_rect.bottom)
+
+ surface.lock() # For possible speed up.
+
+ if match_color:
+ pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) == color]
+ else:
+ pts = [(x, y) for x in x_range for y in y_range if get_at((x, y)) != color]
+
+ surface.unlock()
+ return pts
+
+
+def create_bounding_rect(surface, surf_color, default_pos):
+ """Create a rect to bound all the pixels that don't match surf_color.
+
+ The default_pos parameter is used to position the bounding rect for the
+ case where all pixels match the surf_color.
+ """
+ width, height = surface.get_clip().size
+ xmin, ymin = width, height
+ xmax, ymax = -1, -1
+ get_at = surface.get_at # For possible speed up.
+
+ surface.lock() # For possible speed up.
+
+ for y in range(height):
+ for x in range(width):
+ if get_at((x, y)) != surf_color:
+ xmin = min(x, xmin)
+ xmax = max(x, xmax)
+ ymin = min(y, ymin)
+ ymax = max(y, ymax)
+
+ surface.unlock()
+
+ if -1 == xmax:
+ # No points means a 0 sized rect positioned at default_pos.
+ return pygame.Rect(default_pos, (0, 0))
+ return pygame.Rect((xmin, ymin), (xmax - xmin + 1, ymax - ymin + 1))
+
+
+class InvalidBool:
+ """To help test invalid bool values."""
+
+ __bool__ = None
+
+
+class DrawTestCase(unittest.TestCase):
+ """Base class to test draw module functions."""
+
+ draw_rect = staticmethod(draw.rect)
+ draw_polygon = staticmethod(draw.polygon)
+ draw_circle = staticmethod(draw.circle)
+ draw_ellipse = staticmethod(draw.ellipse)
+ draw_arc = staticmethod(draw.arc)
+ draw_line = staticmethod(draw.line)
+ draw_lines = staticmethod(draw.lines)
+ draw_aaline = staticmethod(draw.aaline)
+ draw_aalines = staticmethod(draw.aalines)
+
+
+class PythonDrawTestCase(unittest.TestCase):
+ """Base class to test draw_py module functions."""
+
+ # draw_py is currently missing some functions.
+ # draw_rect = staticmethod(draw_py.draw_rect)
+ draw_polygon = staticmethod(draw_py.draw_polygon)
+ # draw_circle = staticmethod(draw_py.draw_circle)
+ # draw_ellipse = staticmethod(draw_py.draw_ellipse)
+ # draw_arc = staticmethod(draw_py.draw_arc)
+ draw_line = staticmethod(draw_py.draw_line)
+ draw_lines = staticmethod(draw_py.draw_lines)
+ draw_aaline = staticmethod(draw_py.draw_aaline)
+ draw_aalines = staticmethod(draw_py.draw_aalines)
+
+
+### Ellipse Testing ###########################################################
+
+
+class DrawEllipseMixin:
+ """Mixin tests for drawing ellipses.
+
+ This class contains all the general ellipse drawing tests.
+ """
+
+ def test_ellipse__args(self):
+ """Ensures draw ellipse accepts the correct args."""
+ bounds_rect = self.draw_ellipse(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((0, 0), (3, 2)), 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__args_without_width(self):
+ """Ensures draw ellipse accepts the args without a width."""
+ bounds_rect = self.draw_ellipse(
+ pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((1, 1), (1, 1))
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__args_with_negative_width(self):
+ """Ensures draw ellipse accepts the args with negative width."""
+ bounds_rect = self.draw_ellipse(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), pygame.Rect((2, 3), (3, 2)), -1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+ self.assertEqual(bounds_rect, pygame.Rect(2, 3, 0, 0))
+
+ def test_ellipse__args_with_width_gt_radius(self):
+ """Ensures draw ellipse accepts the args with
+ width > rect.w // 2 and width > rect.h // 2.
+ """
+ rect = pygame.Rect((0, 0), (4, 4))
+ bounds_rect = self.draw_ellipse(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.w // 2 + 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ bounds_rect = self.draw_ellipse(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), rect, rect.h // 2 + 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__kwargs(self):
+ """Ensures draw ellipse accepts the correct kwargs
+ with and without a width arg.
+ """
+ kwargs_list = [
+ {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("yellow"),
+ "rect": pygame.Rect((0, 0), (3, 2)),
+ "width": 1,
+ },
+ {
+ "surface": pygame.Surface((2, 1)),
+ "color": (0, 10, 20),
+ "rect": (0, 0, 1, 1),
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_ellipse(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__kwargs_order_independent(self):
+ """Ensures draw ellipse's kwargs are not order dependent."""
+ bounds_rect = self.draw_ellipse(
+ color=(1, 2, 3),
+ surface=pygame.Surface((3, 2)),
+ width=0,
+ rect=pygame.Rect((1, 0), (1, 1)),
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__args_missing(self):
+ """Ensures draw ellipse detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_ellipse(surface, pygame.Color("red"))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_ellipse(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_ellipse()
+
+ def test_ellipse__kwargs_missing(self):
+ """Ensures draw ellipse detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((1, 2)),
+ "color": pygame.Color("red"),
+ "rect": pygame.Rect((1, 0), (2, 2)),
+ "width": 2,
+ }
+
+ for name in ("rect", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_ellipse(**invalid_kwargs)
+
+ def test_ellipse__arg_invalid_types(self):
+ """Ensures draw ellipse detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ rect = pygame.Rect((1, 1), (1, 1))
+
+ with self.assertRaises(TypeError):
+ # Invalid width.
+ bounds_rect = self.draw_ellipse(surface, color, rect, "1")
+
+ with self.assertRaises(TypeError):
+ # Invalid rect.
+ bounds_rect = self.draw_ellipse(surface, color, (1, 2, 3, 4, 5), 1)
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_ellipse(surface, 2.3, rect, 0)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_ellipse(rect, color, rect, 2)
+
+ def test_ellipse__kwarg_invalid_types(self):
+ """Ensures draw ellipse detects invalid kwarg types."""
+ surface = pygame.Surface((3, 3))
+ color = pygame.Color("green")
+ rect = pygame.Rect((0, 1), (1, 1))
+ kwargs_list = [
+ {
+ "surface": pygame.Surface, # Invalid surface.
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": 2.3, # Invalid color.
+ "rect": rect,
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": (0, 0, 0), # Invalid rect.
+ "width": 1,
+ },
+ {"surface": surface, "color": color, "rect": rect, "width": 1.1},
+ ] # Invalid width.
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_ellipse(**kwargs)
+
+ def test_ellipse__kwarg_invalid_name(self):
+ """Ensures draw ellipse detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ rect = pygame.Rect((0, 1), (2, 2))
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "invalid": 1,
+ },
+ {"surface": surface, "color": color, "rect": rect, "invalid": 1},
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_ellipse(**kwargs)
+
+ def test_ellipse__args_and_kwargs(self):
+ """Ensures draw ellipse accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 1))
+ color = (255, 255, 0, 0)
+ rect = pygame.Rect((1, 0), (2, 1))
+ width = 0
+ kwargs = {"surface": surface, "color": color, "rect": rect, "width": width}
+
+ for name in ("surface", "color", "rect", "width"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_ellipse(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_ellipse(surface, color, **kwargs)
+ elif "rect" == name:
+ bounds_rect = self.draw_ellipse(surface, color, rect, **kwargs)
+ else:
+ bounds_rect = self.draw_ellipse(surface, color, rect, width, **kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__valid_width_values(self):
+ """Ensures draw ellipse accepts different width values."""
+ pos = (1, 1)
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ color = (10, 20, 30, 255)
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "rect": pygame.Rect(pos, (3, 2)),
+ "width": None,
+ }
+
+ for width in (-1000, -10, -1, 0, 1, 10, 1000):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["width"] = width
+ expected_color = color if width >= 0 else surface_color
+
+ bounds_rect = self.draw_ellipse(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__valid_rect_formats(self):
+ """Ensures draw ellipse accepts different rect formats."""
+ pos = (1, 1)
+ expected_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((4, 4))
+ kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0}
+ rects = (pygame.Rect(pos, (1, 3)), (pos, (2, 1)), (pos[0], pos[1], 1, 1))
+
+ for rect in rects:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["rect"] = rect
+
+ bounds_rect = self.draw_ellipse(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__valid_color_formats(self):
+ """Ensures draw ellipse accepts different color formats."""
+ pos = (1, 1)
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "rect": pygame.Rect(pos, (1, 2)),
+ "width": 0,
+ }
+ reds = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in reds:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_ellipse(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_ellipse__invalid_color_formats(self):
+ """Ensures draw ellipse handles invalid color formats correctly."""
+ pos = (1, 1)
+ surface = pygame.Surface((4, 3))
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "rect": pygame.Rect(pos, (2, 2)),
+ "width": 1,
+ }
+
+ for expected_color in (2.3, surface):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_ellipse(**kwargs)
+
+ def test_ellipse(self):
+ """Tests ellipses of differing sizes on surfaces of differing sizes.
+
+ Checks if the number of sides touching the border of the surface is
+ correct.
+ """
+ left_top = [(0, 0), (1, 0), (0, 1), (1, 1)]
+ sizes = [(4, 4), (5, 4), (4, 5), (5, 5)]
+ color = (1, 13, 24, 255)
+
+ def same_size(width, height, border_width):
+ """Test for ellipses with the same size as the surface."""
+ surface = pygame.Surface((width, height))
+
+ self.draw_ellipse(surface, color, (0, 0, width, height), border_width)
+
+ # For each of the four borders check if it contains the color
+ borders = get_border_values(surface, width, height)
+ for border in borders:
+ self.assertTrue(color in border)
+
+ def not_same_size(width, height, border_width, left, top):
+ """Test for ellipses that aren't the same size as the surface."""
+ surface = pygame.Surface((width, height))
+
+ self.draw_ellipse(
+ surface, color, (left, top, width - 1, height - 1), border_width
+ )
+
+ borders = get_border_values(surface, width, height)
+
+ # Check if two sides of the ellipse are touching the border
+ sides_touching = [color in border for border in borders].count(True)
+ self.assertEqual(sides_touching, 2)
+
+ for width, height in sizes:
+ for border_width in (0, 1):
+ same_size(width, height, border_width)
+ for left, top in left_top:
+ not_same_size(width, height, border_width, left, top)
+
+ def test_ellipse__big_ellipse(self):
+ """Test for big ellipse that could overflow in algorithm"""
+ width = 1025
+ height = 1025
+ border = 1
+ x_value_test = int(0.4 * height)
+ y_value_test = int(0.4 * height)
+ surface = pygame.Surface((width, height))
+
+ self.draw_ellipse(surface, (255, 0, 0), (0, 0, width, height), border)
+ colored_pixels = 0
+ for y in range(height):
+ if surface.get_at((x_value_test, y)) == (255, 0, 0):
+ colored_pixels += 1
+ for x in range(width):
+ if surface.get_at((x, y_value_test)) == (255, 0, 0):
+ colored_pixels += 1
+ self.assertEqual(colored_pixels, border * 4)
+
+ def test_ellipse__thick_line(self):
+ """Ensures a thick lined ellipse is drawn correctly."""
+ ellipse_color = pygame.Color("yellow")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((40, 40))
+ rect = pygame.Rect((0, 0), (31, 23))
+ rect.center = surface.get_rect().center
+
+ # As the lines get thicker the internals of the ellipse are not
+ # cleanly defined. So only test up to a few thicknesses before the
+ # maximum thickness.
+ for thickness in range(1, min(*rect.size) // 2 - 2):
+ surface.fill(surface_color) # Clear for each test.
+
+ self.draw_ellipse(surface, ellipse_color, rect, thickness)
+
+ surface.lock() # For possible speed up.
+
+ # Check vertical thickness on the ellipse's top.
+ x = rect.centerx
+ y_start = rect.top
+ y_end = rect.top + thickness - 1
+
+ for y in range(y_start, y_end + 1):
+ self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness)
+
+ # Check pixels above and below this line.
+ self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness)
+ self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness)
+
+ # Check vertical thickness on the ellipse's bottom.
+ x = rect.centerx
+ y_start = rect.bottom - thickness
+ y_end = rect.bottom - 1
+
+ for y in range(y_start, y_end + 1):
+ self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness)
+
+ # Check pixels above and below this line.
+ self.assertEqual(surface.get_at((x, y_start - 1)), surface_color, thickness)
+ self.assertEqual(surface.get_at((x, y_end + 1)), surface_color, thickness)
+
+ # Check horizontal thickness on the ellipse's left.
+ x_start = rect.left
+ x_end = rect.left + thickness - 1
+ y = rect.centery
+
+ for x in range(x_start, x_end + 1):
+ self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness)
+
+ # Check pixels to the left and right of this line.
+ self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness)
+ self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness)
+
+ # Check horizontal thickness on the ellipse's right.
+ x_start = rect.right - thickness
+ x_end = rect.right - 1
+ y = rect.centery
+
+ for x in range(x_start, x_end + 1):
+ self.assertEqual(surface.get_at((x, y)), ellipse_color, thickness)
+
+ # Check pixels to the left and right of this line.
+ self.assertEqual(surface.get_at((x_start - 1, y)), surface_color, thickness)
+ self.assertEqual(surface.get_at((x_end + 1, y)), surface_color, thickness)
+
+ surface.unlock()
+
+ def test_ellipse__no_holes(self):
+ width = 80
+ height = 70
+ surface = pygame.Surface((width + 1, height))
+ rect = pygame.Rect(0, 0, width, height)
+ for thickness in range(1, 37, 5):
+ surface.fill("BLACK")
+ self.draw_ellipse(surface, "RED", rect, thickness)
+ for y in range(height):
+ number_of_changes = 0
+ drawn_pixel = False
+ for x in range(width + 1):
+ if (
+ not drawn_pixel
+ and surface.get_at((x, y)) == pygame.Color("RED")
+ or drawn_pixel
+ and surface.get_at((x, y)) == pygame.Color("BLACK")
+ ):
+ drawn_pixel = not drawn_pixel
+ number_of_changes += 1
+ if y < thickness or y > height - thickness - 1:
+ self.assertEqual(number_of_changes, 2)
+ else:
+ self.assertEqual(number_of_changes, 4)
+
+ def test_ellipse__max_width(self):
+ """Ensures an ellipse with max width (and greater) is drawn correctly."""
+ ellipse_color = pygame.Color("yellow")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((40, 40))
+ rect = pygame.Rect((0, 0), (31, 21))
+ rect.center = surface.get_rect().center
+ max_thickness = (min(*rect.size) + 1) // 2
+
+ for thickness in range(max_thickness, max_thickness + 3):
+ surface.fill(surface_color) # Clear for each test.
+
+ self.draw_ellipse(surface, ellipse_color, rect, thickness)
+
+ surface.lock() # For possible speed up.
+
+ # Check vertical thickness.
+ for y in range(rect.top, rect.bottom):
+ self.assertEqual(surface.get_at((rect.centerx, y)), ellipse_color)
+
+ # Check horizontal thickness.
+ for x in range(rect.left, rect.right):
+ self.assertEqual(surface.get_at((x, rect.centery)), ellipse_color)
+
+ # Check pixels above and below ellipse.
+ self.assertEqual(
+ surface.get_at((rect.centerx, rect.top - 1)), surface_color
+ )
+ self.assertEqual(
+ surface.get_at((rect.centerx, rect.bottom + 1)), surface_color
+ )
+
+ # Check pixels to the left and right of the ellipse.
+ self.assertEqual(
+ surface.get_at((rect.left - 1, rect.centery)), surface_color
+ )
+ self.assertEqual(
+ surface.get_at((rect.right + 1, rect.centery)), surface_color
+ )
+
+ surface.unlock()
+
+ def _check_1_pixel_sized_ellipse(
+ self, surface, collide_rect, surface_color, ellipse_color
+ ):
+ # Helper method to check the surface for 1 pixel wide and/or high
+ # ellipses.
+ surf_w, surf_h = surface.get_size()
+
+ surface.lock() # For possible speed up.
+
+ for pos in ((x, y) for y in range(surf_h) for x in range(surf_w)):
+ # Since the ellipse is just a line we can use a rect to help find
+ # where it is expected to be drawn.
+ if collide_rect.collidepoint(pos):
+ expected_color = ellipse_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(
+ surface.get_at(pos),
+ expected_color,
+ f"collide_rect={collide_rect}, pos={pos}",
+ )
+
+ surface.unlock()
+
+ def test_ellipse__1_pixel_width(self):
+ """Ensures an ellipse with a width of 1 is drawn correctly.
+
+ An ellipse with a width of 1 pixel is a vertical line.
+ """
+ ellipse_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surf_w, surf_h = 10, 20
+
+ surface = pygame.Surface((surf_w, surf_h))
+ rect = pygame.Rect((0, 0), (1, 0))
+ collide_rect = rect.copy()
+
+ # Calculate some positions.
+ off_left = -1
+ off_right = surf_w
+ off_bottom = surf_h
+ center_x = surf_w // 2
+ center_y = surf_h // 2
+
+ # Test some even and odd heights.
+ for ellipse_h in range(6, 10):
+ collide_rect.h = ellipse_h
+ rect.h = ellipse_h
+
+ # Calculate some variable positions.
+ off_top = -(ellipse_h + 1)
+ half_off_top = -(ellipse_h // 2)
+ half_off_bottom = surf_h - (ellipse_h // 2)
+
+ # Draw the ellipse in different positions: fully on-surface,
+ # partially off-surface, and fully off-surface.
+ positions = (
+ (off_left, off_top),
+ (off_left, half_off_top),
+ (off_left, center_y),
+ (off_left, half_off_bottom),
+ (off_left, off_bottom),
+ (center_x, off_top),
+ (center_x, half_off_top),
+ (center_x, center_y),
+ (center_x, half_off_bottom),
+ (center_x, off_bottom),
+ (off_right, off_top),
+ (off_right, half_off_top),
+ (off_right, center_y),
+ (off_right, half_off_bottom),
+ (off_right, off_bottom),
+ )
+
+ for rect_pos in positions:
+ surface.fill(surface_color) # Clear before each draw.
+ rect.topleft = rect_pos
+ collide_rect.topleft = rect_pos
+
+ self.draw_ellipse(surface, ellipse_color, rect)
+
+ self._check_1_pixel_sized_ellipse(
+ surface, collide_rect, surface_color, ellipse_color
+ )
+
+ def test_ellipse__1_pixel_width_spanning_surface(self):
+ """Ensures an ellipse with a width of 1 is drawn correctly
+ when spanning the height of the surface.
+
+ An ellipse with a width of 1 pixel is a vertical line.
+ """
+ ellipse_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surf_w, surf_h = 10, 20
+
+ surface = pygame.Surface((surf_w, surf_h))
+ rect = pygame.Rect((0, 0), (1, surf_h + 2)) # Longer than the surface.
+
+ # Draw the ellipse in different positions: on-surface and off-surface.
+ positions = (
+ (-1, -1), # (off_left, off_top)
+ (0, -1), # (left_edge, off_top)
+ (surf_w // 2, -1), # (center_x, off_top)
+ (surf_w - 1, -1), # (right_edge, off_top)
+ (surf_w, -1),
+ ) # (off_right, off_top)
+
+ for rect_pos in positions:
+ surface.fill(surface_color) # Clear before each draw.
+ rect.topleft = rect_pos
+
+ self.draw_ellipse(surface, ellipse_color, rect)
+
+ self._check_1_pixel_sized_ellipse(
+ surface, rect, surface_color, ellipse_color
+ )
+
+ def test_ellipse__1_pixel_height(self):
+ """Ensures an ellipse with a height of 1 is drawn correctly.
+
+ An ellipse with a height of 1 pixel is a horizontal line.
+ """
+ ellipse_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surf_w, surf_h = 20, 10
+
+ surface = pygame.Surface((surf_w, surf_h))
+ rect = pygame.Rect((0, 0), (0, 1))
+ collide_rect = rect.copy()
+
+ # Calculate some positions.
+ off_right = surf_w
+ off_top = -1
+ off_bottom = surf_h
+ center_x = surf_w // 2
+ center_y = surf_h // 2
+
+ # Test some even and odd widths.
+ for ellipse_w in range(6, 10):
+ collide_rect.w = ellipse_w
+ rect.w = ellipse_w
+
+ # Calculate some variable positions.
+ off_left = -(ellipse_w + 1)
+ half_off_left = -(ellipse_w // 2)
+ half_off_right = surf_w - (ellipse_w // 2)
+
+ # Draw the ellipse in different positions: fully on-surface,
+ # partially off-surface, and fully off-surface.
+ positions = (
+ (off_left, off_top),
+ (half_off_left, off_top),
+ (center_x, off_top),
+ (half_off_right, off_top),
+ (off_right, off_top),
+ (off_left, center_y),
+ (half_off_left, center_y),
+ (center_x, center_y),
+ (half_off_right, center_y),
+ (off_right, center_y),
+ (off_left, off_bottom),
+ (half_off_left, off_bottom),
+ (center_x, off_bottom),
+ (half_off_right, off_bottom),
+ (off_right, off_bottom),
+ )
+
+ for rect_pos in positions:
+ surface.fill(surface_color) # Clear before each draw.
+ rect.topleft = rect_pos
+ collide_rect.topleft = rect_pos
+
+ self.draw_ellipse(surface, ellipse_color, rect)
+
+ self._check_1_pixel_sized_ellipse(
+ surface, collide_rect, surface_color, ellipse_color
+ )
+
+ def test_ellipse__1_pixel_height_spanning_surface(self):
+ """Ensures an ellipse with a height of 1 is drawn correctly
+ when spanning the width of the surface.
+
+ An ellipse with a height of 1 pixel is a horizontal line.
+ """
+ ellipse_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surf_w, surf_h = 20, 10
+
+ surface = pygame.Surface((surf_w, surf_h))
+ rect = pygame.Rect((0, 0), (surf_w + 2, 1)) # Wider than the surface.
+
+ # Draw the ellipse in different positions: on-surface and off-surface.
+ positions = (
+ (-1, -1), # (off_left, off_top)
+ (-1, 0), # (off_left, top_edge)
+ (-1, surf_h // 2), # (off_left, center_y)
+ (-1, surf_h - 1), # (off_left, bottom_edge)
+ (-1, surf_h),
+ ) # (off_left, off_bottom)
+
+ for rect_pos in positions:
+ surface.fill(surface_color) # Clear before each draw.
+ rect.topleft = rect_pos
+
+ self.draw_ellipse(surface, ellipse_color, rect)
+
+ self._check_1_pixel_sized_ellipse(
+ surface, rect, surface_color, ellipse_color
+ )
+
+ def test_ellipse__1_pixel_width_and_height(self):
+ """Ensures an ellipse with a width and height of 1 is drawn correctly.
+
+ An ellipse with a width and height of 1 pixel is a single pixel.
+ """
+ ellipse_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surf_w, surf_h = 10, 10
+
+ surface = pygame.Surface((surf_w, surf_h))
+ rect = pygame.Rect((0, 0), (1, 1))
+
+ # Calculate some positions.
+ off_left = -1
+ off_right = surf_w
+ off_top = -1
+ off_bottom = surf_h
+ left_edge = 0
+ right_edge = surf_w - 1
+ top_edge = 0
+ bottom_edge = surf_h - 1
+ center_x = surf_w // 2
+ center_y = surf_h // 2
+
+ # Draw the ellipse in different positions: center surface,
+ # top/bottom/left/right edges, and off-surface.
+ positions = (
+ (off_left, off_top),
+ (off_left, top_edge),
+ (off_left, center_y),
+ (off_left, bottom_edge),
+ (off_left, off_bottom),
+ (left_edge, off_top),
+ (left_edge, top_edge),
+ (left_edge, center_y),
+ (left_edge, bottom_edge),
+ (left_edge, off_bottom),
+ (center_x, off_top),
+ (center_x, top_edge),
+ (center_x, center_y),
+ (center_x, bottom_edge),
+ (center_x, off_bottom),
+ (right_edge, off_top),
+ (right_edge, top_edge),
+ (right_edge, center_y),
+ (right_edge, bottom_edge),
+ (right_edge, off_bottom),
+ (off_right, off_top),
+ (off_right, top_edge),
+ (off_right, center_y),
+ (off_right, bottom_edge),
+ (off_right, off_bottom),
+ )
+
+ for rect_pos in positions:
+ surface.fill(surface_color) # Clear before each draw.
+ rect.topleft = rect_pos
+
+ self.draw_ellipse(surface, ellipse_color, rect)
+
+ self._check_1_pixel_sized_ellipse(
+ surface, rect, surface_color, ellipse_color
+ )
+
+ def test_ellipse__bounding_rect(self):
+ """Ensures draw ellipse returns the correct bounding rect.
+
+ Tests ellipses on and off the surface and a range of width/thickness
+ values.
+ """
+ ellipse_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ min_width = min_height = 5
+ max_width = max_height = 7
+ sizes = ((min_width, min_height), (max_width, max_height))
+ surface = pygame.Surface((20, 20), 0, 32)
+ surf_rect = surface.get_rect()
+ # Make a rect that is bigger than the surface to help test drawing
+ # ellipses off and partially off the surface.
+ big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1)
+
+ for pos in rect_corners_mids_and_center(
+ surf_rect
+ ) + rect_corners_mids_and_center(big_rect):
+ # Each of the ellipse's rect position attributes will be set to
+ # the pos value.
+ for attr in RECT_POSITION_ATTRIBUTES:
+ # Test using different rect sizes and thickness values.
+ for width, height in sizes:
+ ellipse_rect = pygame.Rect((0, 0), (width, height))
+ setattr(ellipse_rect, attr, pos)
+
+ for thickness in (0, 1, 2, 3, min(width, height)):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_ellipse(
+ surface, ellipse_color, ellipse_rect, thickness
+ )
+
+ # Calculating the expected_rect after the ellipse
+ # is drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(
+ surface, surf_color, ellipse_rect.topleft
+ )
+
+ self.assertEqual(bounding_rect, expected_rect)
+
+ def test_ellipse__surface_clip(self):
+ """Ensures draw ellipse respects a surface's clip area.
+
+ Tests drawing the ellipse filled and unfilled.
+ """
+ surfw = surfh = 30
+ ellipse_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (11, 11))
+ clip_rect.center = surface.get_rect().center
+ pos_rect = clip_rect.copy() # Manages the ellipse's pos.
+
+ for width in (0, 1): # Filled and unfilled.
+ # Test centering the ellipse along the clip rect's edge.
+ for center in rect_corners_mids_and_center(clip_rect):
+ # Get the expected points by drawing the ellipse without the
+ # clip area set.
+ pos_rect.center = center
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_ellipse(surface, ellipse_color, pos_rect, width)
+ expected_pts = get_color_points(surface, ellipse_color, clip_rect)
+
+ # Clear the surface and set the clip area. Redraw the ellipse
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_ellipse(surface, ellipse_color, pos_rect, width)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure only the expected_pts
+ # are the ellipse_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ expected_color = ellipse_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(surface.get_at(pt), expected_color, pt)
+
+ surface.unlock()
+
+
+class DrawEllipseTest(DrawEllipseMixin, DrawTestCase):
+ """Test draw module function ellipse.
+
+ This class inherits the general tests from DrawEllipseMixin. It is also
+ the class to add any draw.ellipse specific tests to.
+ """
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever properly supports drawing ellipses.
+# @unittest.skip('draw_py.draw_ellipse not supported yet')
+# class PythonDrawEllipseTest(DrawEllipseMixin, PythonDrawTestCase):
+# """Test draw_py module function draw_ellipse.
+#
+# This class inherits the general tests from DrawEllipseMixin. It is also
+# the class to add any draw_py.draw_ellipse specific tests to.
+# """
+
+
+### Line/Lines/AALine/AALines Testing #########################################
+
+
+class BaseLineMixin:
+ """Mixin base for drawing various lines.
+
+ This class contains general helper methods and setup for testing the
+ different types of lines.
+ """
+
+ COLORS = (
+ (0, 0, 0),
+ (255, 0, 0),
+ (0, 255, 0),
+ (0, 0, 255),
+ (255, 255, 0),
+ (255, 0, 255),
+ (0, 255, 255),
+ (255, 255, 255),
+ )
+
+ @staticmethod
+ def _create_surfaces():
+ # Create some surfaces with different sizes, depths, and flags.
+ surfaces = []
+ for size in ((49, 49), (50, 50)):
+ for depth in (8, 16, 24, 32):
+ for flags in (0, SRCALPHA):
+ surface = pygame.display.set_mode(size, flags, depth)
+ surfaces.append(surface)
+ surfaces.append(surface.convert_alpha())
+ return surfaces
+
+ @staticmethod
+ def _rect_lines(rect):
+ # Yields pairs of end points and their reverse (to test symmetry).
+ # Uses a rect with the points radiating from its midleft.
+ for pt in rect_corners_mids_and_center(rect):
+ if pt in [rect.midleft, rect.center]:
+ # Don't bother with these points.
+ continue
+ yield (rect.midleft, pt)
+ yield (pt, rect.midleft)
+
+
+### Line Testing ##############################################################
+
+
+class LineMixin(BaseLineMixin):
+ """Mixin test for drawing a single line.
+
+ This class contains all the general single line drawing tests.
+ """
+
+ def test_line__args(self):
+ """Ensures draw line accepts the correct args."""
+ bounds_rect = self.draw_line(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__args_without_width(self):
+ """Ensures draw line accepts the args without a width."""
+ bounds_rect = self.draw_line(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2)
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__kwargs(self):
+ """Ensures draw line accepts the correct kwargs
+ with and without a width arg.
+ """
+ surface = pygame.Surface((4, 4))
+ color = pygame.Color("yellow")
+ start_pos = (1, 1)
+ end_pos = (2, 2)
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_line(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__kwargs_order_independent(self):
+ """Ensures draw line's kwargs are not order dependent."""
+ bounds_rect = self.draw_line(
+ start_pos=(1, 2),
+ end_pos=(2, 1),
+ width=2,
+ color=(10, 20, 30),
+ surface=pygame.Surface((3, 2)),
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__args_missing(self):
+ """Ensures draw line detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+ color = pygame.Color("blue")
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(surface, color, (0, 0))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(surface, color)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line()
+
+ def test_line__kwargs_missing(self):
+ """Ensures draw line detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((3, 2)),
+ "color": pygame.Color("red"),
+ "start_pos": (2, 1),
+ "end_pos": (2, 2),
+ "width": 1,
+ }
+
+ for name in ("end_pos", "start_pos", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(**invalid_kwargs)
+
+ def test_line__arg_invalid_types(self):
+ """Ensures draw line detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ start_pos = (0, 1)
+ end_pos = (1, 2)
+
+ with self.assertRaises(TypeError):
+ # Invalid width.
+ bounds_rect = self.draw_line(surface, color, start_pos, end_pos, "1")
+
+ with self.assertRaises(TypeError):
+ # Invalid end_pos.
+ bounds_rect = self.draw_line(surface, color, start_pos, (1, 2, 3))
+
+ with self.assertRaises(TypeError):
+ # Invalid start_pos.
+ bounds_rect = self.draw_line(surface, color, (1,), end_pos)
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_line(surface, 2.3, start_pos, end_pos)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_line((1, 2, 3, 4), color, start_pos, end_pos)
+
+ def test_line__kwarg_invalid_types(self):
+ """Ensures draw line detects invalid kwarg types."""
+ surface = pygame.Surface((3, 3))
+ color = pygame.Color("green")
+ start_pos = (1, 0)
+ end_pos = (2, 0)
+ width = 1
+ kwargs_list = [
+ {
+ "surface": pygame.Surface, # Invalid surface.
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "width": width,
+ },
+ {
+ "surface": surface,
+ "color": 2.3, # Invalid color.
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "width": width,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": (0, 0, 0), # Invalid start_pos.
+ "end_pos": end_pos,
+ "width": width,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": (0,), # Invalid end_pos.
+ "width": width,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "width": 1.2,
+ },
+ ] # Invalid width.
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(**kwargs)
+
+ def test_line__kwarg_invalid_name(self):
+ """Ensures draw line detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ start_pos = (1, 1)
+ end_pos = (2, 0)
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "width": 1,
+ "invalid": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "invalid": 1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(**kwargs)
+
+ def test_line__args_and_kwargs(self):
+ """Ensures draw line accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 2))
+ color = (255, 255, 0, 0)
+ start_pos = (0, 1)
+ end_pos = (1, 2)
+ width = 0
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "width": width,
+ }
+
+ for name in ("surface", "color", "start_pos", "end_pos", "width"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_line(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_line(surface, color, **kwargs)
+ elif "start_pos" == name:
+ bounds_rect = self.draw_line(surface, color, start_pos, **kwargs)
+ elif "end_pos" == name:
+ bounds_rect = self.draw_line(
+ surface, color, start_pos, end_pos, **kwargs
+ )
+ else:
+ bounds_rect = self.draw_line(
+ surface, color, start_pos, end_pos, width, **kwargs
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__valid_width_values(self):
+ """Ensures draw line accepts different width values."""
+ line_color = pygame.Color("yellow")
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ pos = (2, 1)
+ kwargs = {
+ "surface": surface,
+ "color": line_color,
+ "start_pos": pos,
+ "end_pos": (2, 2),
+ "width": None,
+ }
+
+ for width in (-100, -10, -1, 0, 1, 10, 100):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["width"] = width
+ expected_color = line_color if width > 0 else surface_color
+
+ bounds_rect = self.draw_line(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__valid_start_pos_formats(self):
+ """Ensures draw line accepts different start_pos formats."""
+ expected_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((4, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "start_pos": None,
+ "end_pos": (2, 2),
+ "width": 2,
+ }
+ x, y = 2, 1 # start position
+
+ # The point values can be ints or floats.
+ for start_pos in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)):
+ # The point type can be a tuple/list/Vector2.
+ for seq_type in (tuple, list, Vector2):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["start_pos"] = seq_type(start_pos)
+
+ bounds_rect = self.draw_line(**kwargs)
+
+ self.assertEqual(surface.get_at((x, y)), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__valid_end_pos_formats(self):
+ """Ensures draw line accepts different end_pos formats."""
+ expected_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((4, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "start_pos": (2, 1),
+ "end_pos": None,
+ "width": 2,
+ }
+ x, y = 2, 2 # end position
+
+ # The point values can be ints or floats.
+ for end_pos in ((x, y), (x + 0.2, y), (x, y + 0.2), (x + 0.2, y + 0.2)):
+ # The point type can be a tuple/list/Vector2.
+ for seq_type in (tuple, list, Vector2):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["end_pos"] = seq_type(end_pos)
+
+ bounds_rect = self.draw_line(**kwargs)
+
+ self.assertEqual(surface.get_at((x, y)), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__invalid_start_pos_formats(self):
+ """Ensures draw line handles invalid start_pos formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "start_pos": None,
+ "end_pos": (2, 2),
+ "width": 1,
+ }
+
+ start_pos_fmts = (
+ (2,), # Too few coords.
+ (2, 1, 0), # Too many coords.
+ (2, "1"), # Wrong type.
+ {2, 1}, # Wrong type.
+ {2: 1},
+ ) # Wrong type.
+
+ for start_pos in start_pos_fmts:
+ kwargs["start_pos"] = start_pos
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(**kwargs)
+
+ def test_line__invalid_end_pos_formats(self):
+ """Ensures draw line handles invalid end_pos formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "start_pos": (2, 2),
+ "end_pos": None,
+ "width": 1,
+ }
+
+ end_pos_fmts = (
+ (2,), # Too few coords.
+ (2, 1, 0), # Too many coords.
+ (2, "1"), # Wrong type.
+ {2, 1}, # Wrong type.
+ {2: 1},
+ ) # Wrong type.
+
+ for end_pos in end_pos_fmts:
+ kwargs["end_pos"] = end_pos
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(**kwargs)
+
+ def test_line__valid_color_formats(self):
+ """Ensures draw line accepts different color formats."""
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ pos = (1, 1)
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "start_pos": pos,
+ "end_pos": (2, 1),
+ "width": 3,
+ }
+ greens = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in greens:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_line(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_line__invalid_color_formats(self):
+ """Ensures draw line handles invalid color formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 3)),
+ "color": None,
+ "start_pos": (1, 1),
+ "end_pos": (2, 1),
+ "width": 1,
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_line(**kwargs)
+
+ def test_line__color(self):
+ """Tests if the line drawn is the correct color."""
+ pos = (0, 0)
+ for surface in self._create_surfaces():
+ for expected_color in self.COLORS:
+ self.draw_line(surface, expected_color, pos, (1, 0))
+
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+
+ def test_line__color_with_thickness(self):
+ """Ensures a thick line is drawn using the correct color."""
+ from_x = 5
+ to_x = 10
+ y = 5
+ for surface in self._create_surfaces():
+ for expected_color in self.COLORS:
+ self.draw_line(surface, expected_color, (from_x, y), (to_x, y), 5)
+ for pos in ((x, y + i) for i in (-2, 0, 2) for x in (from_x, to_x)):
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+
+ def test_line__gaps(self):
+ """Tests if the line drawn contains any gaps."""
+ expected_color = (255, 255, 255)
+ for surface in self._create_surfaces():
+ width = surface.get_width()
+ self.draw_line(surface, expected_color, (0, 0), (width - 1, 0))
+
+ for x in range(width):
+ pos = (x, 0)
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+
+ def test_line__gaps_with_thickness(self):
+ """Ensures a thick line is drawn without any gaps."""
+ expected_color = (255, 255, 255)
+ thickness = 5
+ for surface in self._create_surfaces():
+ width = surface.get_width() - 1
+ h = width // 5
+ w = h * 5
+ self.draw_line(surface, expected_color, (0, 5), (w, 5 + h), thickness)
+
+ for x in range(w + 1):
+ for y in range(3, 8):
+ pos = (x, y + ((x + 2) // 5))
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+
+ def test_line__bounding_rect(self):
+ """Ensures draw line returns the correct bounding rect.
+
+ Tests lines with endpoints on and off the surface and a range of
+ width/thickness values.
+ """
+ if isinstance(self, PythonDrawTestCase):
+ self.skipTest("bounding rects not supported in draw_py.draw_line")
+
+ line_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ width = height = 30
+ # Using a rect to help manage where the lines are drawn.
+ helper_rect = pygame.Rect((0, 0), (width, height))
+
+ # Testing surfaces of different sizes. One larger than the helper_rect
+ # and one smaller (to test lines that span the surface).
+ for size in ((width + 5, height + 5), (width - 5, height - 5)):
+ surface = pygame.Surface(size, 0, 32)
+ surf_rect = surface.get_rect()
+
+ # Move the helper rect to different positions to test line
+ # endpoints on and off the surface.
+ for pos in rect_corners_mids_and_center(surf_rect):
+ helper_rect.center = pos
+
+ # Draw using different thicknesses.
+ for thickness in range(-1, 5):
+ for start, end in self._rect_lines(helper_rect):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_line(
+ surface, line_color, start, end, thickness
+ )
+
+ if 0 < thickness:
+ # Calculating the expected_rect after the line is
+ # drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(
+ surface, surf_color, start
+ )
+ else:
+ # Nothing drawn.
+ expected_rect = pygame.Rect(start, (0, 0))
+
+ self.assertEqual(
+ bounding_rect,
+ expected_rect,
+ "start={}, end={}, size={}, thickness={}".format(
+ start, end, size, thickness
+ ),
+ )
+
+ def test_line__surface_clip(self):
+ """Ensures draw line respects a surface's clip area."""
+ surfw = surfh = 30
+ line_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (11, 11))
+ clip_rect.center = surface.get_rect().center
+ pos_rect = clip_rect.copy() # Manages the line's pos.
+
+ for thickness in (1, 3): # Test different line widths.
+ # Test centering the line along the clip rect's edge.
+ for center in rect_corners_mids_and_center(clip_rect):
+ # Get the expected points by drawing the line without the
+ # clip area set.
+ pos_rect.center = center
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_line(
+ surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness
+ )
+ expected_pts = get_color_points(surface, line_color, clip_rect)
+
+ # Clear the surface and set the clip area. Redraw the line
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_line(
+ surface, line_color, pos_rect.midtop, pos_rect.midbottom, thickness
+ )
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure only the expected_pts
+ # are the line_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ expected_color = line_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(surface.get_at(pt), expected_color, pt)
+
+ surface.unlock()
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever fully supports drawing single lines.
+# @unittest.skip('draw_py.draw_line not fully supported yet')
+# class PythonDrawLineTest(LineMixin, PythonDrawTestCase):
+# """Test draw_py module function line.
+#
+# This class inherits the general tests from LineMixin. It is also the class
+# to add any draw_py.draw_line specific tests to.
+# """
+
+
+class DrawLineTest(LineMixin, DrawTestCase):
+ """Test draw module function line.
+
+ This class inherits the general tests from LineMixin. It is also the class
+ to add any draw.line specific tests to.
+ """
+
+ def test_line_endianness(self):
+ """test color component order"""
+ for depth in (24, 32):
+ surface = pygame.Surface((5, 3), 0, depth)
+ surface.fill(pygame.Color(0, 0, 0))
+ self.draw_line(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1)
+
+ self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here")
+
+ surface.fill(pygame.Color(0, 0, 0))
+ self.draw_line(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1)
+
+ self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here")
+
+ def test_line(self):
+ # (l, t), (l, t)
+ self.surf_size = (320, 200)
+ self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA)
+ self.color = (1, 13, 24, 205)
+
+ drawn = draw.line(self.surf, self.color, (1, 0), (200, 0))
+ self.assertEqual(
+ drawn.right, 201, "end point arg should be (or at least was) inclusive"
+ )
+
+ # Should be colored where it's supposed to be
+ for pt in test_utils.rect_area_pts(drawn):
+ self.assertEqual(self.surf.get_at(pt), self.color)
+
+ # And not where it shouldn't
+ for pt in test_utils.rect_outer_bounds(drawn):
+ self.assertNotEqual(self.surf.get_at(pt), self.color)
+
+ # Line width greater that 1
+ line_width = 2
+ offset = 5
+ a = (offset, offset)
+ b = (self.surf_size[0] - offset, a[1])
+ c = (a[0], self.surf_size[1] - offset)
+ d = (b[0], c[1])
+ e = (a[0] + offset, c[1])
+ f = (b[0], c[0] + 5)
+ lines = [
+ (a, d),
+ (b, c),
+ (c, b),
+ (d, a),
+ (a, b),
+ (b, a),
+ (a, c),
+ (c, a),
+ (a, e),
+ (e, a),
+ (a, f),
+ (f, a),
+ (a, a),
+ ]
+
+ for p1, p2 in lines:
+ msg = f"{p1} - {p2}"
+ if p1[0] <= p2[0]:
+ plow = p1
+ phigh = p2
+ else:
+ plow = p2
+ phigh = p1
+
+ self.surf.fill((0, 0, 0))
+ rec = draw.line(self.surf, (255, 255, 255), p1, p2, line_width)
+ xinc = yinc = 0
+
+ if abs(p1[0] - p2[0]) > abs(p1[1] - p2[1]):
+ yinc = 1
+ else:
+ xinc = 1
+
+ for i in range(line_width):
+ p = (p1[0] + xinc * i, p1[1] + yinc * i)
+ self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg)
+
+ p = (p2[0] + xinc * i, p2[1] + yinc * i)
+ self.assertEqual(self.surf.get_at(p), (255, 255, 255), msg)
+
+ p = (plow[0] - 1, plow[1])
+ self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg)
+
+ p = (plow[0] + xinc * line_width, plow[1] + yinc * line_width)
+ self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg)
+
+ p = (phigh[0] + xinc * line_width, phigh[1] + yinc * line_width)
+ self.assertEqual(self.surf.get_at(p), (0, 0, 0), msg)
+
+ if p1[0] < p2[0]:
+ rx = p1[0]
+ else:
+ rx = p2[0]
+
+ if p1[1] < p2[1]:
+ ry = p1[1]
+ else:
+ ry = p2[1]
+
+ w = abs(p2[0] - p1[0]) + 1 + xinc * (line_width - 1)
+ h = abs(p2[1] - p1[1]) + 1 + yinc * (line_width - 1)
+ msg += f", {rec}"
+
+ self.assertEqual(rec, (rx, ry, w, h), msg)
+
+ def test_line_for_gaps(self):
+ # This checks bug Thick Line Bug #448
+
+ width = 200
+ height = 200
+ surf = pygame.Surface((width, height), pygame.SRCALPHA)
+
+ def white_surrounded_pixels(x, y):
+ offsets = [(1, 0), (0, 1), (-1, 0), (0, -1)]
+ WHITE = (255, 255, 255, 255)
+ return len(
+ [1 for dx, dy in offsets if surf.get_at((x + dx, y + dy)) == WHITE]
+ )
+
+ def check_white_line(start, end):
+ surf.fill((0, 0, 0))
+ pygame.draw.line(surf, (255, 255, 255), start, end, 30)
+
+ BLACK = (0, 0, 0, 255)
+ for x in range(1, width - 1):
+ for y in range(1, height - 1):
+ if surf.get_at((x, y)) == BLACK:
+ self.assertTrue(white_surrounded_pixels(x, y) < 3)
+
+ check_white_line((50, 50), (140, 0))
+ check_white_line((50, 50), (0, 120))
+ check_white_line((50, 50), (199, 198))
+
+
+### Lines Testing #############################################################
+
+
+class LinesMixin(BaseLineMixin):
+ """Mixin test for drawing lines.
+
+ This class contains all the general lines drawing tests.
+ """
+
+ def test_lines__args(self):
+ """Ensures draw lines accepts the correct args."""
+ bounds_rect = self.draw_lines(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__args_without_width(self):
+ """Ensures draw lines accepts the args without a width."""
+ bounds_rect = self.draw_lines(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1))
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__kwargs(self):
+ """Ensures draw lines accepts the correct kwargs
+ with and without a width arg.
+ """
+ surface = pygame.Surface((4, 4))
+ color = pygame.Color("yellow")
+ points = ((0, 0), (1, 1), (2, 2))
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "closed": False,
+ "points": points,
+ "width": 1,
+ },
+ {"surface": surface, "color": color, "closed": False, "points": points},
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_lines(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__kwargs_order_independent(self):
+ """Ensures draw lines's kwargs are not order dependent."""
+ bounds_rect = self.draw_lines(
+ closed=1,
+ points=((0, 0), (1, 1), (2, 2)),
+ width=2,
+ color=(10, 20, 30),
+ surface=pygame.Surface((3, 2)),
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__args_missing(self):
+ """Ensures draw lines detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+ color = pygame.Color("blue")
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(surface, color, 0)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(surface, color)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines()
+
+ def test_lines__kwargs_missing(self):
+ """Ensures draw lines detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((3, 2)),
+ "color": pygame.Color("red"),
+ "closed": 1,
+ "points": ((2, 2), (1, 1)),
+ "width": 1,
+ }
+
+ for name in ("points", "closed", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(**invalid_kwargs)
+
+ def test_lines__arg_invalid_types(self):
+ """Ensures draw lines detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ closed = 0
+ points = ((1, 2), (2, 1))
+
+ with self.assertRaises(TypeError):
+ # Invalid width.
+ bounds_rect = self.draw_lines(surface, color, closed, points, "1")
+
+ with self.assertRaises(TypeError):
+ # Invalid points.
+ bounds_rect = self.draw_lines(surface, color, closed, (1, 2, 3))
+
+ with self.assertRaises(TypeError):
+ # Invalid closed.
+ bounds_rect = self.draw_lines(surface, color, InvalidBool(), points)
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_lines(surface, 2.3, closed, points)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_lines((1, 2, 3, 4), color, closed, points)
+
+ def test_lines__kwarg_invalid_types(self):
+ """Ensures draw lines detects invalid kwarg types."""
+ valid_kwargs = {
+ "surface": pygame.Surface((3, 3)),
+ "color": pygame.Color("green"),
+ "closed": False,
+ "points": ((1, 2), (2, 1)),
+ "width": 1,
+ }
+
+ invalid_kwargs = {
+ "surface": pygame.Surface,
+ "color": 2.3,
+ "closed": InvalidBool(),
+ "points": (0, 0, 0),
+ "width": 1.2,
+ }
+
+ for kwarg in ("surface", "color", "closed", "points", "width"):
+ kwargs = dict(valid_kwargs)
+ kwargs[kwarg] = invalid_kwargs[kwarg]
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(**kwargs)
+
+ def test_lines__kwarg_invalid_name(self):
+ """Ensures draw lines detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ closed = 1
+ points = ((1, 2), (2, 1))
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "closed": closed,
+ "points": points,
+ "width": 1,
+ "invalid": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "closed": closed,
+ "points": points,
+ "invalid": 1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(**kwargs)
+
+ def test_lines__args_and_kwargs(self):
+ """Ensures draw lines accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 2))
+ color = (255, 255, 0, 0)
+ closed = 0
+ points = ((1, 2), (2, 1))
+ width = 1
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "closed": closed,
+ "points": points,
+ "width": width,
+ }
+
+ for name in ("surface", "color", "closed", "points", "width"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_lines(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_lines(surface, color, **kwargs)
+ elif "closed" == name:
+ bounds_rect = self.draw_lines(surface, color, closed, **kwargs)
+ elif "points" == name:
+ bounds_rect = self.draw_lines(surface, color, closed, points, **kwargs)
+ else:
+ bounds_rect = self.draw_lines(
+ surface, color, closed, points, width, **kwargs
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__valid_width_values(self):
+ """Ensures draw lines accepts different width values."""
+ line_color = pygame.Color("yellow")
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ pos = (1, 1)
+ kwargs = {
+ "surface": surface,
+ "color": line_color,
+ "closed": False,
+ "points": (pos, (2, 1)),
+ "width": None,
+ }
+
+ for width in (-100, -10, -1, 0, 1, 10, 100):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["width"] = width
+ expected_color = line_color if width > 0 else surface_color
+
+ bounds_rect = self.draw_lines(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__valid_points_format(self):
+ """Ensures draw lines accepts different points formats."""
+ expected_color = (10, 20, 30, 255)
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "closed": False,
+ "points": None,
+ "width": 1,
+ }
+
+ # The point type can be a tuple/list/Vector2.
+ point_types = (
+ (tuple, tuple, tuple, tuple), # all tuples
+ (list, list, list, list), # all lists
+ (Vector2, Vector2, Vector2, Vector2), # all Vector2s
+ (list, Vector2, tuple, Vector2),
+ ) # mix
+
+ # The point values can be ints or floats.
+ point_values = (
+ ((1, 1), (2, 1), (2, 2), (1, 2)),
+ ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)),
+ )
+
+ # Each sequence of points can be a tuple or a list.
+ seq_types = (tuple, list)
+
+ for point_type in point_types:
+ for values in point_values:
+ check_pos = values[0]
+ points = [point_type[i](pt) for i, pt in enumerate(values)]
+
+ for seq_type in seq_types:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["points"] = seq_type(points)
+
+ bounds_rect = self.draw_lines(**kwargs)
+
+ self.assertEqual(surface.get_at(check_pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__invalid_points_formats(self):
+ """Ensures draw lines handles invalid points formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "closed": False,
+ "points": None,
+ "width": 1,
+ }
+
+ points_fmts = (
+ ((1, 1), (2,)), # Too few coords.
+ ((1, 1), (2, 2, 2)), # Too many coords.
+ ((1, 1), (2, "2")), # Wrong type.
+ ((1, 1), {2, 3}), # Wrong type.
+ ((1, 1), {2: 2, 3: 3}), # Wrong type.
+ {(1, 1), (1, 2)}, # Wrong type.
+ {1: 1, 4: 4},
+ ) # Wrong type.
+
+ for points in points_fmts:
+ kwargs["points"] = points
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(**kwargs)
+
+ def test_lines__invalid_points_values(self):
+ """Ensures draw lines handles invalid points values correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "closed": False,
+ "points": None,
+ "width": 1,
+ }
+
+ for points in ([], ((1, 1),)): # Too few points.
+ for seq_type in (tuple, list): # Test as tuples and lists.
+ kwargs["points"] = seq_type(points)
+
+ with self.assertRaises(ValueError):
+ bounds_rect = self.draw_lines(**kwargs)
+
+ def test_lines__valid_closed_values(self):
+ """Ensures draw lines accepts different closed values."""
+ line_color = pygame.Color("blue")
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ pos = (1, 2)
+ kwargs = {
+ "surface": surface,
+ "color": line_color,
+ "closed": None,
+ "points": ((1, 1), (3, 1), (3, 3), (1, 3)),
+ "width": 1,
+ }
+
+ true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True)
+ false_values = (None, "", 0, (), [], False)
+
+ for closed in true_values + false_values:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["closed"] = closed
+ expected_color = line_color if closed else surface_color
+
+ bounds_rect = self.draw_lines(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__valid_color_formats(self):
+ """Ensures draw lines accepts different color formats."""
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ pos = (1, 1)
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "closed": False,
+ "points": (pos, (2, 1)),
+ "width": 3,
+ }
+ greens = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in greens:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_lines(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_lines__invalid_color_formats(self):
+ """Ensures draw lines handles invalid color formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 3)),
+ "color": None,
+ "closed": False,
+ "points": ((1, 1), (1, 2)),
+ "width": 1,
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_lines(**kwargs)
+
+ def test_lines__color(self):
+ """Tests if the lines drawn are the correct color.
+
+ Draws lines around the border of the given surface and checks if all
+ borders of the surface only contain the given color.
+ """
+ for surface in self._create_surfaces():
+ for expected_color in self.COLORS:
+ self.draw_lines(surface, expected_color, True, corners(surface))
+
+ for pos, color in border_pos_and_color(surface):
+ self.assertEqual(color, expected_color, f"pos={pos}")
+
+ def test_lines__color_with_thickness(self):
+ """Ensures thick lines are drawn using the correct color."""
+ x_left = y_top = 5
+ for surface in self._create_surfaces():
+ x_right = surface.get_width() - 5
+ y_bottom = surface.get_height() - 5
+ endpoints = (
+ (x_left, y_top),
+ (x_right, y_top),
+ (x_right, y_bottom),
+ (x_left, y_bottom),
+ )
+ for expected_color in self.COLORS:
+ self.draw_lines(surface, expected_color, True, endpoints, 3)
+
+ for t in (-1, 0, 1):
+ for x in range(x_left, x_right + 1):
+ for y in (y_top, y_bottom):
+ pos = (x, y + t)
+ self.assertEqual(
+ surface.get_at(pos),
+ expected_color,
+ f"pos={pos}",
+ )
+ for y in range(y_top, y_bottom + 1):
+ for x in (x_left, x_right):
+ pos = (x + t, y)
+ self.assertEqual(
+ surface.get_at(pos),
+ expected_color,
+ f"pos={pos}",
+ )
+
+ def test_lines__gaps(self):
+ """Tests if the lines drawn contain any gaps.
+
+ Draws lines around the border of the given surface and checks if
+ all borders of the surface contain any gaps.
+ """
+ expected_color = (255, 255, 255)
+ for surface in self._create_surfaces():
+ self.draw_lines(surface, expected_color, True, corners(surface))
+
+ for pos, color in border_pos_and_color(surface):
+ self.assertEqual(color, expected_color, f"pos={pos}")
+
+ def test_lines__gaps_with_thickness(self):
+ """Ensures thick lines are drawn without any gaps."""
+ expected_color = (255, 255, 255)
+ x_left = y_top = 5
+ for surface in self._create_surfaces():
+ h = (surface.get_width() - 11) // 5
+ w = h * 5
+ x_right = x_left + w
+ y_bottom = y_top + h
+ endpoints = ((x_left, y_top), (x_right, y_top), (x_right, y_bottom))
+ self.draw_lines(surface, expected_color, True, endpoints, 3)
+
+ for x in range(x_left, x_right + 1):
+ for t in (-1, 0, 1):
+ pos = (x, y_top + t)
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+ pos = (x, y_top + t + ((x - 3) // 5))
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+ for y in range(y_top, y_bottom + 1):
+ for t in (-1, 0, 1):
+ pos = (x_right + t, y)
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+
+ def test_lines__bounding_rect(self):
+ """Ensures draw lines returns the correct bounding rect.
+
+ Tests lines with endpoints on and off the surface and a range of
+ width/thickness values.
+ """
+ line_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ width = height = 30
+ # Using a rect to help manage where the lines are drawn.
+ pos_rect = pygame.Rect((0, 0), (width, height))
+
+ # Testing surfaces of different sizes. One larger than the pos_rect
+ # and one smaller (to test lines that span the surface).
+ for size in ((width + 5, height + 5), (width - 5, height - 5)):
+ surface = pygame.Surface(size, 0, 32)
+ surf_rect = surface.get_rect()
+
+ # Move pos_rect to different positions to test line endpoints on
+ # and off the surface.
+ for pos in rect_corners_mids_and_center(surf_rect):
+ pos_rect.center = pos
+ # Shape: Triangle (if closed), ^ caret (if not closed).
+ pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright)
+ pos = pts[0] # Rect position if nothing drawn.
+
+ # Draw using different thickness and closed values.
+ for thickness in range(-1, 5):
+ for closed in (True, False):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_lines(
+ surface, line_color, closed, pts, thickness
+ )
+
+ if 0 < thickness:
+ # Calculating the expected_rect after the lines are
+ # drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(
+ surface, surf_color, pos
+ )
+ else:
+ # Nothing drawn.
+ expected_rect = pygame.Rect(pos, (0, 0))
+
+ self.assertEqual(bounding_rect, expected_rect)
+
+ def test_lines__surface_clip(self):
+ """Ensures draw lines respects a surface's clip area."""
+ surfw = surfh = 30
+ line_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (11, 11))
+ clip_rect.center = surface.get_rect().center
+ pos_rect = clip_rect.copy() # Manages the lines's pos.
+
+ # Test centering the pos_rect along the clip rect's edge to allow for
+ # drawing the lines over the clip_rect's bounds.
+ for center in rect_corners_mids_and_center(clip_rect):
+ pos_rect.center = center
+ pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom)
+
+ for closed in (True, False): # Test closed and not closed.
+ for thickness in (1, 3): # Test different line widths.
+ # Get the expected points by drawing the lines without the
+ # clip area set.
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_lines(surface, line_color, closed, pts, thickness)
+ expected_pts = get_color_points(surface, line_color, clip_rect)
+
+ # Clear the surface and set the clip area. Redraw the lines
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_lines(surface, line_color, closed, pts, thickness)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure only the
+ # expected_pts are the line_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ expected_color = line_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(surface.get_at(pt), expected_color, pt)
+
+ surface.unlock()
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever fully supports drawing lines.
+# class PythonDrawLinesTest(LinesMixin, PythonDrawTestCase):
+# """Test draw_py module function lines.
+#
+# This class inherits the general tests from LinesMixin. It is also the
+# class to add any draw_py.draw_lines specific tests to.
+# """
+
+
+class DrawLinesTest(LinesMixin, DrawTestCase):
+ """Test draw module function lines.
+
+ This class inherits the general tests from LinesMixin. It is also the class
+ to add any draw.lines specific tests to.
+ """
+
+
+### AALine Testing ############################################################
+
+
+class AALineMixin(BaseLineMixin):
+ """Mixin test for drawing a single aaline.
+
+ This class contains all the general single aaline drawing tests.
+ """
+
+ def test_aaline__args(self):
+ """Ensures draw aaline accepts the correct args."""
+ bounds_rect = self.draw_aaline(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), (1, 1), 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aaline__args_without_blend(self):
+ """Ensures draw aaline accepts the args without a blend."""
+ bounds_rect = self.draw_aaline(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2)
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aaline__blend_warning(self):
+ """From pygame 2, blend=False should raise DeprecationWarning."""
+ with warnings.catch_warnings(record=True) as w:
+ # Cause all warnings to always be triggered.
+ warnings.simplefilter("always")
+ # Trigger DeprecationWarning.
+ self.draw_aaline(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), (0, 0), (2, 2), False
+ )
+ # Check if there is only one warning and is a DeprecationWarning.
+ self.assertEqual(len(w), 1)
+ self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
+
+ def test_aaline__kwargs(self):
+ """Ensures draw aaline accepts the correct kwargs"""
+ surface = pygame.Surface((4, 4))
+ color = pygame.Color("yellow")
+ start_pos = (1, 1)
+ end_pos = (2, 2)
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aaline__kwargs_order_independent(self):
+ """Ensures draw aaline's kwargs are not order dependent."""
+ bounds_rect = self.draw_aaline(
+ start_pos=(1, 2),
+ end_pos=(2, 1),
+ color=(10, 20, 30),
+ surface=pygame.Surface((3, 2)),
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aaline__args_missing(self):
+ """Ensures draw aaline detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+ color = pygame.Color("blue")
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(surface, color, (0, 0))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(surface, color)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline()
+
+ def test_aaline__kwargs_missing(self):
+ """Ensures draw aaline detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((3, 2)),
+ "color": pygame.Color("red"),
+ "start_pos": (2, 1),
+ "end_pos": (2, 2),
+ }
+
+ for name in ("end_pos", "start_pos", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(**invalid_kwargs)
+
+ def test_aaline__arg_invalid_types(self):
+ """Ensures draw aaline detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ start_pos = (0, 1)
+ end_pos = (1, 2)
+
+ with self.assertRaises(TypeError):
+ # Invalid end_pos.
+ bounds_rect = self.draw_aaline(surface, color, start_pos, (1, 2, 3))
+
+ with self.assertRaises(TypeError):
+ # Invalid start_pos.
+ bounds_rect = self.draw_aaline(surface, color, (1,), end_pos)
+
+ with self.assertRaises(ValueError):
+ # Invalid color.
+ bounds_rect = self.draw_aaline(surface, "invalid-color", start_pos, end_pos)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_aaline((1, 2, 3, 4), color, start_pos, end_pos)
+
+ def test_aaline__kwarg_invalid_types(self):
+ """Ensures draw aaline detects invalid kwarg types."""
+ surface = pygame.Surface((3, 3))
+ color = pygame.Color("green")
+ start_pos = (1, 0)
+ end_pos = (2, 0)
+ kwargs_list = [
+ {
+ "surface": pygame.Surface, # Invalid surface.
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ },
+ {
+ "surface": surface,
+ "color": 2.3, # Invalid color.
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": (0, 0, 0), # Invalid start_pos.
+ "end_pos": end_pos,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": (0,), # Invalid end_pos.
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ def test_aaline__kwarg_invalid_name(self):
+ """Ensures draw aaline detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ start_pos = (1, 1)
+ end_pos = (2, 0)
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "invalid": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ "invalid": 1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ def test_aaline__args_and_kwargs(self):
+ """Ensures draw aaline accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 2))
+ color = (255, 255, 0, 0)
+ start_pos = (0, 1)
+ end_pos = (1, 2)
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "start_pos": start_pos,
+ "end_pos": end_pos,
+ }
+
+ for name in ("surface", "color", "start_pos", "end_pos"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_aaline(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_aaline(surface, color, **kwargs)
+ elif "start_pos" == name:
+ bounds_rect = self.draw_aaline(surface, color, start_pos, **kwargs)
+ elif "end_pos" == name:
+ bounds_rect = self.draw_aaline(
+ surface, color, start_pos, end_pos, **kwargs
+ )
+ else:
+ bounds_rect = self.draw_aaline(
+ surface, color, start_pos, end_pos, **kwargs
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aaline__valid_start_pos_formats(self):
+ """Ensures draw aaline accepts different start_pos formats."""
+ expected_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((4, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "start_pos": None,
+ "end_pos": (2, 2),
+ }
+ x, y = 2, 1 # start position
+ positions = ((x, y), (x + 0.01, y), (x, y + 0.01), (x + 0.01, y + 0.01))
+
+ for start_pos in positions:
+ for seq_type in (tuple, list, Vector2):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["start_pos"] = seq_type(start_pos)
+
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ color = surface.get_at((x, y))
+ for i, sub_color in enumerate(expected_color):
+ # The color could be slightly off the expected color due to
+ # any fractional position arguments.
+ self.assertGreaterEqual(color[i] + 6, sub_color, start_pos)
+ self.assertIsInstance(bounds_rect, pygame.Rect, start_pos)
+
+ def test_aaline__valid_end_pos_formats(self):
+ """Ensures draw aaline accepts different end_pos formats."""
+ expected_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((4, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "start_pos": (2, 1),
+ "end_pos": None,
+ }
+ x, y = 2, 2 # end position
+ positions = ((x, y), (x + 0.02, y), (x, y + 0.02), (x + 0.02, y + 0.02))
+
+ for end_pos in positions:
+ for seq_type in (tuple, list, Vector2):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["end_pos"] = seq_type(end_pos)
+
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ color = surface.get_at((x, y))
+ for i, sub_color in enumerate(expected_color):
+ # The color could be slightly off the expected color due to
+ # any fractional position arguments.
+ self.assertGreaterEqual(color[i] + 15, sub_color, end_pos)
+ self.assertIsInstance(bounds_rect, pygame.Rect, end_pos)
+
+ def test_aaline__invalid_start_pos_formats(self):
+ """Ensures draw aaline handles invalid start_pos formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "start_pos": None,
+ "end_pos": (2, 2),
+ }
+
+ start_pos_fmts = (
+ (2,), # Too few coords.
+ (2, 1, 0), # Too many coords.
+ (2, "1"), # Wrong type.
+ {2, 1}, # Wrong type.
+ {2: 1},
+ ) # Wrong type.
+
+ for start_pos in start_pos_fmts:
+ kwargs["start_pos"] = start_pos
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ def test_aaline__invalid_end_pos_formats(self):
+ """Ensures draw aaline handles invalid end_pos formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "start_pos": (2, 2),
+ "end_pos": None,
+ }
+
+ end_pos_fmts = (
+ (2,), # Too few coords.
+ (2, 1, 0), # Too many coords.
+ (2, "1"), # Wrong type.
+ {2, 1}, # Wrong type.
+ {2: 1},
+ ) # Wrong type.
+
+ for end_pos in end_pos_fmts:
+ kwargs["end_pos"] = end_pos
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ def test_aaline__valid_color_formats(self):
+ """Ensures draw aaline accepts different color formats."""
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ pos = (1, 1)
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "start_pos": pos,
+ "end_pos": (2, 1),
+ }
+ greens = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in greens:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aaline__invalid_color_formats(self):
+ """Ensures draw aaline handles invalid color formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 3)),
+ "color": None,
+ "start_pos": (1, 1),
+ "end_pos": (2, 1),
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aaline(**kwargs)
+
+ def test_aaline__color(self):
+ """Tests if the aaline drawn is the correct color."""
+ pos = (0, 0)
+ for surface in self._create_surfaces():
+ for expected_color in self.COLORS:
+ self.draw_aaline(surface, expected_color, pos, (1, 0))
+
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+
+ def test_aaline__gaps(self):
+ """Tests if the aaline drawn contains any gaps.
+
+ See: #512
+ """
+ expected_color = (255, 255, 255)
+ for surface in self._create_surfaces():
+ width = surface.get_width()
+ self.draw_aaline(surface, expected_color, (0, 0), (width - 1, 0))
+
+ for x in range(width):
+ pos = (x, 0)
+ self.assertEqual(surface.get_at(pos), expected_color, f"pos={pos}")
+
+ def test_aaline__bounding_rect(self):
+ """Ensures draw aaline returns the correct bounding rect.
+
+ Tests lines with endpoints on and off the surface.
+ """
+ line_color = pygame.Color("red")
+ surf_color = pygame.Color("blue")
+ width = height = 30
+ # Using a rect to help manage where the lines are drawn.
+ helper_rect = pygame.Rect((0, 0), (width, height))
+
+ # Testing surfaces of different sizes. One larger than the helper_rect
+ # and one smaller (to test lines that span the surface).
+ for size in ((width + 5, height + 5), (width - 5, height - 5)):
+ surface = pygame.Surface(size, 0, 32)
+ surf_rect = surface.get_rect()
+
+ # Move the helper rect to different positions to test line
+ # endpoints on and off the surface.
+ for pos in rect_corners_mids_and_center(surf_rect):
+ helper_rect.center = pos
+
+ for start, end in self._rect_lines(helper_rect):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_aaline(surface, line_color, start, end)
+
+ # Calculating the expected_rect after the line is
+ # drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(surface, surf_color, start)
+
+ self.assertEqual(bounding_rect, expected_rect)
+
+ def test_aaline__surface_clip(self):
+ """Ensures draw aaline respects a surface's clip area."""
+ surfw = surfh = 30
+ aaline_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (11, 11))
+ clip_rect.center = surface.get_rect().center
+ pos_rect = clip_rect.copy() # Manages the aaline's pos.
+
+ # Test centering the pos_rect along the clip rect's edge to allow for
+ # drawing the aaline over the clip_rect's bounds.
+ for center in rect_corners_mids_and_center(clip_rect):
+ pos_rect.center = center
+
+ # Get the expected points by drawing the aaline without the
+ # clip area set.
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom)
+
+ expected_pts = get_color_points(surface, surface_color, clip_rect, False)
+
+ # Clear the surface and set the clip area. Redraw the aaline
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_aaline(surface, aaline_color, pos_rect.midtop, pos_rect.midbottom)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure the expected_pts
+ # are not surface_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ self.assertNotEqual(surface.get_at(pt), surface_color, pt)
+ else:
+ self.assertEqual(surface.get_at(pt), surface_color, pt)
+
+ surface.unlock()
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever fully supports drawing single aalines.
+# class PythonDrawAALineTest(AALineMixin, PythonDrawTestCase):
+# """Test draw_py module function aaline.
+#
+# This class inherits the general tests from AALineMixin. It is also the
+# class to add any draw_py.draw_aaline specific tests to.
+# """
+
+
+class DrawAALineTest(AALineMixin, DrawTestCase):
+ """Test draw module function aaline.
+
+ This class inherits the general tests from AALineMixin. It is also the
+ class to add any draw.aaline specific tests to.
+ """
+
+ def test_aaline_endianness(self):
+ """test color component order"""
+ for depth in (24, 32):
+ surface = pygame.Surface((5, 3), 0, depth)
+ surface.fill(pygame.Color(0, 0, 0))
+ self.draw_aaline(surface, pygame.Color(255, 0, 0), (0, 1), (2, 1), 1)
+
+ self.assertGreater(surface.get_at((1, 1)).r, 0, "there should be red here")
+
+ surface.fill(pygame.Color(0, 0, 0))
+ self.draw_aaline(surface, pygame.Color(0, 0, 255), (0, 1), (2, 1), 1)
+
+ self.assertGreater(surface.get_at((1, 1)).b, 0, "there should be blue here")
+
+ def _check_antialiasing(
+ self, from_point, to_point, should, check_points, set_endpoints=True
+ ):
+ """Draw a line between two points and check colors of check_points."""
+ if set_endpoints:
+ should[from_point] = should[to_point] = FG_GREEN
+
+ def check_one_direction(from_point, to_point, should):
+ self.draw_aaline(self.surface, FG_GREEN, from_point, to_point, True)
+
+ for pt in check_points:
+ color = should.get(pt, BG_RED)
+ with self.subTest(from_pt=from_point, pt=pt, to=to_point):
+ self.assertEqual(self.surface.get_at(pt), color)
+
+ # reset
+ draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0)
+
+ # it is important to test also opposite direction, the algorithm
+ # is (#512) or was not symmetric
+ check_one_direction(from_point, to_point, should)
+ if from_point != to_point:
+ check_one_direction(to_point, from_point, should)
+
+ def test_short_non_antialiased_lines(self):
+ """test very short not anti aliased lines in all directions."""
+
+ # Horizontal, vertical and diagonal lines should not be anti-aliased,
+ # even with draw.aaline ...
+ self.surface = pygame.Surface((10, 10))
+ draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0)
+
+ check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)]
+
+ def check_both_directions(from_pt, to_pt, other_points):
+ should = {pt: FG_GREEN for pt in other_points}
+ self._check_antialiasing(from_pt, to_pt, should, check_points)
+
+ # 0. one point
+ check_both_directions((5, 5), (5, 5), [])
+ # 1. horizontal
+ check_both_directions((4, 7), (5, 7), [])
+ check_both_directions((5, 4), (7, 4), [(6, 4)])
+
+ # 2. vertical
+ check_both_directions((5, 5), (5, 6), [])
+ check_both_directions((6, 4), (6, 6), [(6, 5)])
+ # 3. diagonals
+ check_both_directions((5, 5), (6, 6), [])
+ check_both_directions((5, 5), (7, 7), [(6, 6)])
+ check_both_directions((5, 6), (6, 5), [])
+ check_both_directions((6, 4), (4, 6), [(5, 5)])
+
+ def test_short_line_anti_aliasing(self):
+ self.surface = pygame.Surface((10, 10))
+ draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0)
+
+ check_points = [(i, j) for i in range(3, 8) for j in range(3, 8)]
+
+ def check_both_directions(from_pt, to_pt, should):
+ self._check_antialiasing(from_pt, to_pt, should, check_points)
+
+ brown = (127, 127, 0)
+ reddish = (191, 63, 0)
+ greenish = (63, 191, 0)
+
+ # lets say dx = abs(x0 - x1) ; dy = abs(y0 - y1)
+
+ # dy / dx = 0.5
+ check_both_directions((4, 4), (6, 5), {(5, 4): brown, (5, 5): brown})
+ check_both_directions((4, 5), (6, 4), {(5, 4): brown, (5, 5): brown})
+
+ # dy / dx = 2
+ check_both_directions((4, 4), (5, 6), {(4, 5): brown, (5, 5): brown})
+ check_both_directions((5, 4), (4, 6), {(4, 5): brown, (5, 5): brown})
+
+ # some little longer lines; so we need to check more points:
+ check_points = [(i, j) for i in range(2, 9) for j in range(2, 9)]
+ # dy / dx = 0.25
+ should = {
+ (4, 3): greenish,
+ (5, 3): brown,
+ (6, 3): reddish,
+ (4, 4): reddish,
+ (5, 4): brown,
+ (6, 4): greenish,
+ }
+ check_both_directions((3, 3), (7, 4), should)
+
+ should = {
+ (4, 3): reddish,
+ (5, 3): brown,
+ (6, 3): greenish,
+ (4, 4): greenish,
+ (5, 4): brown,
+ (6, 4): reddish,
+ }
+ check_both_directions((3, 4), (7, 3), should)
+
+ # dy / dx = 4
+ should = {
+ (4, 4): greenish,
+ (4, 5): brown,
+ (4, 6): reddish,
+ (5, 4): reddish,
+ (5, 5): brown,
+ (5, 6): greenish,
+ }
+ check_both_directions((4, 3), (5, 7), should)
+
+ should = {
+ (4, 4): reddish,
+ (4, 5): brown,
+ (4, 6): greenish,
+ (5, 4): greenish,
+ (5, 5): brown,
+ (5, 6): reddish,
+ }
+ check_both_directions((5, 3), (4, 7), should)
+
+ def test_anti_aliasing_float_coordinates(self):
+ """Float coordinates should be blended smoothly."""
+
+ self.surface = pygame.Surface((10, 10))
+ draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0)
+
+ check_points = [(i, j) for i in range(5) for j in range(5)]
+ brown = (127, 127, 0)
+ reddish = (191, 63, 0)
+ greenish = (63, 191, 0)
+
+ # 0. identical point : current implementation does no smoothing...
+ expected = {(2, 2): FG_GREEN}
+ self._check_antialiasing(
+ (1.5, 2), (1.5, 2), expected, check_points, set_endpoints=False
+ )
+ expected = {(2, 3): FG_GREEN}
+ self._check_antialiasing(
+ (2.49, 2.7), (2.49, 2.7), expected, check_points, set_endpoints=False
+ )
+
+ # 1. horizontal lines
+ # a) blend endpoints
+ expected = {(1, 2): brown, (2, 2): FG_GREEN}
+ self._check_antialiasing(
+ (1.5, 2), (2, 2), expected, check_points, set_endpoints=False
+ )
+ expected = {(1, 2): brown, (2, 2): FG_GREEN, (3, 2): brown}
+ self._check_antialiasing(
+ (1.5, 2), (2.5, 2), expected, check_points, set_endpoints=False
+ )
+ expected = {(2, 2): brown, (1, 2): FG_GREEN}
+ self._check_antialiasing(
+ (1, 2), (1.5, 2), expected, check_points, set_endpoints=False
+ )
+ expected = {(1, 2): brown, (2, 2): greenish}
+ self._check_antialiasing(
+ (1.5, 2), (1.75, 2), expected, check_points, set_endpoints=False
+ )
+
+ # b) blend y-coordinate
+ expected = {(x, y): brown for x in range(2, 5) for y in (1, 2)}
+ self._check_antialiasing(
+ (2, 1.5), (4, 1.5), expected, check_points, set_endpoints=False
+ )
+
+ # 2. vertical lines
+ # a) blend endpoints
+ expected = {(2, 1): brown, (2, 2): FG_GREEN, (2, 3): brown}
+ self._check_antialiasing(
+ (2, 1.5), (2, 2.5), expected, check_points, set_endpoints=False
+ )
+ expected = {(2, 1): brown, (2, 2): greenish}
+ self._check_antialiasing(
+ (2, 1.5), (2, 1.75), expected, check_points, set_endpoints=False
+ )
+ # b) blend x-coordinate
+ expected = {(x, y): brown for x in (1, 2) for y in range(2, 5)}
+ self._check_antialiasing(
+ (1.5, 2), (1.5, 4), expected, check_points, set_endpoints=False
+ )
+ # 3. diagonal lines
+ # a) blend endpoints
+ expected = {(1, 1): brown, (2, 2): FG_GREEN, (3, 3): brown}
+ self._check_antialiasing(
+ (1.5, 1.5), (2.5, 2.5), expected, check_points, set_endpoints=False
+ )
+ expected = {(3, 1): brown, (2, 2): FG_GREEN, (1, 3): brown}
+ self._check_antialiasing(
+ (2.5, 1.5), (1.5, 2.5), expected, check_points, set_endpoints=False
+ )
+ # b) blend sidewards
+ expected = {(2, 1): brown, (2, 2): brown, (3, 2): brown, (3, 3): brown}
+ self._check_antialiasing(
+ (2, 1.5), (3, 2.5), expected, check_points, set_endpoints=False
+ )
+
+ expected = {
+ (2, 1): greenish,
+ (2, 2): reddish,
+ (3, 2): greenish,
+ (3, 3): reddish,
+ (4, 3): greenish,
+ (4, 4): reddish,
+ }
+
+ self._check_antialiasing(
+ (2, 1.25), (4, 3.25), expected, check_points, set_endpoints=False
+ )
+
+ def test_anti_aliasing_at_and_outside_the_border(self):
+ """Ensures antialiasing works correct at a surface's borders."""
+
+ self.surface = pygame.Surface((10, 10))
+ draw.rect(self.surface, BG_RED, (0, 0, 10, 10), 0)
+
+ check_points = [(i, j) for i in range(10) for j in range(10)]
+
+ reddish = (191, 63, 0)
+ brown = (127, 127, 0)
+ greenish = (63, 191, 0)
+ from_point, to_point = (3, 3), (7, 4)
+ should = {
+ (4, 3): greenish,
+ (5, 3): brown,
+ (6, 3): reddish,
+ (4, 4): reddish,
+ (5, 4): brown,
+ (6, 4): greenish,
+ }
+
+ for dx, dy in (
+ (-4, 0),
+ (4, 0), # moved to left and right borders
+ (0, -5),
+ (0, -4),
+ (0, -3), # upper border
+ (0, 5),
+ (0, 6),
+ (0, 7), # lower border
+ (-4, -4),
+ (-4, -3),
+ (-3, -4),
+ ): # upper left corner
+ first = from_point[0] + dx, from_point[1] + dy
+ second = to_point[0] + dx, to_point[1] + dy
+ expected = {(x + dx, y + dy): color for (x, y), color in should.items()}
+
+ self._check_antialiasing(first, second, expected, check_points)
+
+
+### AALines Testing ###########################################################
+
+
+class AALinesMixin(BaseLineMixin):
+ """Mixin test for drawing aalines.
+
+ This class contains all the general aalines drawing tests.
+ """
+
+ def test_aalines__args(self):
+ """Ensures draw aalines accepts the correct args."""
+ bounds_rect = self.draw_aalines(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), False, ((0, 0), (1, 1)), 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__args_without_blend(self):
+ """Ensures draw aalines accepts the args without a blend."""
+ bounds_rect = self.draw_aalines(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1))
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__blend_warning(self):
+ """From pygame 2, blend=False should raise DeprecationWarning."""
+ with warnings.catch_warnings(record=True) as w:
+ # Cause all warnings to always be triggered.
+ warnings.simplefilter("always")
+ # Trigger DeprecationWarning.
+ self.draw_aalines(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), False, ((0, 0), (1, 1)), False
+ )
+ # Check if there is only one warning and is a DeprecationWarning.
+ self.assertEqual(len(w), 1)
+ self.assertTrue(issubclass(w[-1].category, DeprecationWarning))
+
+ def test_aalines__kwargs(self):
+ """Ensures draw aalines accepts the correct kwargs."""
+ surface = pygame.Surface((4, 4))
+ color = pygame.Color("yellow")
+ points = ((0, 0), (1, 1), (2, 2))
+ kwargs_list = [
+ {"surface": surface, "color": color, "closed": False, "points": points},
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__kwargs_order_independent(self):
+ """Ensures draw aalines's kwargs are not order dependent."""
+ bounds_rect = self.draw_aalines(
+ closed=1,
+ points=((0, 0), (1, 1), (2, 2)),
+ color=(10, 20, 30),
+ surface=pygame.Surface((3, 2)),
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__args_missing(self):
+ """Ensures draw aalines detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+ color = pygame.Color("blue")
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(surface, color, 0)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(surface, color)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines()
+
+ def test_aalines__kwargs_missing(self):
+ """Ensures draw aalines detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((3, 2)),
+ "color": pygame.Color("red"),
+ "closed": 1,
+ "points": ((2, 2), (1, 1)),
+ }
+
+ for name in ("points", "closed", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(**invalid_kwargs)
+
+ def test_aalines__arg_invalid_types(self):
+ """Ensures draw aalines detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ closed = 0
+ points = ((1, 2), (2, 1))
+
+ with self.assertRaises(TypeError):
+ # Invalid blend.
+ bounds_rect = self.draw_aalines(surface, color, closed, points, "1")
+
+ with self.assertRaises(TypeError):
+ # Invalid points.
+ bounds_rect = self.draw_aalines(surface, color, closed, (1, 2, 3))
+
+ with self.assertRaises(TypeError):
+ # Invalid closed.
+ bounds_rect = self.draw_aalines(surface, color, InvalidBool(), points)
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_aalines(surface, 2.3, closed, points)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_aalines((1, 2, 3, 4), color, closed, points)
+
+ def test_aalines__kwarg_invalid_types(self):
+ """Ensures draw aalines detects invalid kwarg types."""
+ valid_kwargs = {
+ "surface": pygame.Surface((3, 3)),
+ "color": pygame.Color("green"),
+ "closed": False,
+ "points": ((1, 2), (2, 1)),
+ }
+
+ invalid_kwargs = {
+ "surface": pygame.Surface,
+ "color": 2.3,
+ "closed": InvalidBool(),
+ "points": (0, 0, 0),
+ }
+
+ for kwarg in ("surface", "color", "closed", "points"):
+ kwargs = dict(valid_kwargs)
+ kwargs[kwarg] = invalid_kwargs[kwarg]
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ def test_aalines__kwarg_invalid_name(self):
+ """Ensures draw aalines detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ closed = 1
+ points = ((1, 2), (2, 1))
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "closed": closed,
+ "points": points,
+ "invalid": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "closed": closed,
+ "points": points,
+ "invalid": 1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ def test_aalines__args_and_kwargs(self):
+ """Ensures draw aalines accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 2))
+ color = (255, 255, 0, 0)
+ closed = 0
+ points = ((1, 2), (2, 1))
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "closed": closed,
+ "points": points,
+ }
+
+ for name in ("surface", "color", "closed", "points"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_aalines(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_aalines(surface, color, **kwargs)
+ elif "closed" == name:
+ bounds_rect = self.draw_aalines(surface, color, closed, **kwargs)
+ elif "points" == name:
+ bounds_rect = self.draw_aalines(
+ surface, color, closed, points, **kwargs
+ )
+ else:
+ bounds_rect = self.draw_aalines(
+ surface, color, closed, points, **kwargs
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__valid_points_format(self):
+ """Ensures draw aalines accepts different points formats."""
+ expected_color = (10, 20, 30, 255)
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "closed": False,
+ "points": None,
+ }
+
+ # The point type can be a tuple/list/Vector2.
+ point_types = (
+ (tuple, tuple, tuple, tuple), # all tuples
+ (list, list, list, list), # all lists
+ (Vector2, Vector2, Vector2, Vector2), # all Vector2s
+ (list, Vector2, tuple, Vector2),
+ ) # mix
+
+ # The point values can be ints or floats.
+ point_values = (
+ ((1, 1), (2, 1), (2, 2), (1, 2)),
+ ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)),
+ )
+
+ # Each sequence of points can be a tuple or a list.
+ seq_types = (tuple, list)
+
+ for point_type in point_types:
+ for values in point_values:
+ check_pos = values[0]
+ points = [point_type[i](pt) for i, pt in enumerate(values)]
+
+ for seq_type in seq_types:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["points"] = seq_type(points)
+
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ self.assertEqual(surface.get_at(check_pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__invalid_points_formats(self):
+ """Ensures draw aalines handles invalid points formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "closed": False,
+ "points": None,
+ }
+
+ points_fmts = (
+ ((1, 1), (2,)), # Too few coords.
+ ((1, 1), (2, 2, 2)), # Too many coords.
+ ((1, 1), (2, "2")), # Wrong type.
+ ((1, 1), {2, 3}), # Wrong type.
+ ((1, 1), {2: 2, 3: 3}), # Wrong type.
+ {(1, 1), (1, 2)}, # Wrong type.
+ {1: 1, 4: 4},
+ ) # Wrong type.
+
+ for points in points_fmts:
+ kwargs["points"] = points
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ def test_aalines__invalid_points_values(self):
+ """Ensures draw aalines handles invalid points values correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "closed": False,
+ "points": None,
+ }
+
+ for points in ([], ((1, 1),)): # Too few points.
+ for seq_type in (tuple, list): # Test as tuples and lists.
+ kwargs["points"] = seq_type(points)
+
+ with self.assertRaises(ValueError):
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ def test_aalines__valid_closed_values(self):
+ """Ensures draw aalines accepts different closed values."""
+ line_color = pygame.Color("blue")
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((5, 5))
+ pos = (1, 3)
+ kwargs = {
+ "surface": surface,
+ "color": line_color,
+ "closed": None,
+ "points": ((1, 1), (4, 1), (4, 4), (1, 4)),
+ }
+
+ true_values = (-7, 1, 10, "2", 3.1, (4,), [5], True)
+ false_values = (None, "", 0, (), [], False)
+
+ for closed in true_values + false_values:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["closed"] = closed
+ expected_color = line_color if closed else surface_color
+
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__valid_color_formats(self):
+ """Ensures draw aalines accepts different color formats."""
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ pos = (1, 1)
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "closed": False,
+ "points": (pos, (2, 1)),
+ }
+ greens = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in greens:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_aalines__invalid_color_formats(self):
+ """Ensures draw aalines handles invalid color formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 3)),
+ "color": None,
+ "closed": False,
+ "points": ((1, 1), (1, 2)),
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_aalines(**kwargs)
+
+ def test_aalines__color(self):
+ """Tests if the aalines drawn are the correct color.
+
+ Draws aalines around the border of the given surface and checks if all
+ borders of the surface only contain the given color.
+ """
+ for surface in self._create_surfaces():
+ for expected_color in self.COLORS:
+ self.draw_aalines(surface, expected_color, True, corners(surface))
+
+ for pos, color in border_pos_and_color(surface):
+ self.assertEqual(color, expected_color, f"pos={pos}")
+
+ def test_aalines__gaps(self):
+ """Tests if the aalines drawn contain any gaps.
+
+ Draws aalines around the border of the given surface and checks if
+ all borders of the surface contain any gaps.
+
+ See: #512
+ """
+ expected_color = (255, 255, 255)
+ for surface in self._create_surfaces():
+ self.draw_aalines(surface, expected_color, True, corners(surface))
+
+ for pos, color in border_pos_and_color(surface):
+ self.assertEqual(color, expected_color, f"pos={pos}")
+
+ def test_aalines__bounding_rect(self):
+ """Ensures draw aalines returns the correct bounding rect.
+
+ Tests lines with endpoints on and off the surface and blending
+ enabled and disabled.
+ """
+ line_color = pygame.Color("red")
+ surf_color = pygame.Color("blue")
+ width = height = 30
+ # Using a rect to help manage where the lines are drawn.
+ pos_rect = pygame.Rect((0, 0), (width, height))
+
+ # Testing surfaces of different sizes. One larger than the pos_rect
+ # and one smaller (to test lines that span the surface).
+ for size in ((width + 5, height + 5), (width - 5, height - 5)):
+ surface = pygame.Surface(size, 0, 32)
+ surf_rect = surface.get_rect()
+
+ # Move pos_rect to different positions to test line endpoints on
+ # and off the surface.
+ for pos in rect_corners_mids_and_center(surf_rect):
+ pos_rect.center = pos
+ # Shape: Triangle (if closed), ^ caret (if not closed).
+ pts = (pos_rect.midleft, pos_rect.midtop, pos_rect.midright)
+ pos = pts[0] # Rect position if nothing drawn.
+
+ for closed in (True, False):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_aalines(surface, line_color, closed, pts)
+
+ # Calculating the expected_rect after the lines are
+ # drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(surface, surf_color, pos)
+
+ self.assertEqual(bounding_rect, expected_rect)
+
+ def test_aalines__surface_clip(self):
+ """Ensures draw aalines respects a surface's clip area."""
+ surfw = surfh = 30
+ aaline_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (11, 11))
+ clip_rect.center = surface.get_rect().center
+ pos_rect = clip_rect.copy() # Manages the aalines's pos.
+
+ # Test centering the pos_rect along the clip rect's edge to allow for
+ # drawing the aalines over the clip_rect's bounds.
+ for center in rect_corners_mids_and_center(clip_rect):
+ pos_rect.center = center
+ pts = (pos_rect.midtop, pos_rect.center, pos_rect.midbottom)
+ for closed in (True, False): # Test closed and not closed.
+ # Get the expected points by drawing the aalines without
+ # the clip area set.
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_aalines(surface, aaline_color, closed, pts)
+
+ expected_pts = get_color_points(
+ surface, surface_color, clip_rect, False
+ )
+
+ # Clear the surface and set the clip area. Redraw the
+ # aalines and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_aalines(surface, aaline_color, closed, pts)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure the expected_pts
+ # are not surface_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ self.assertNotEqual(surface.get_at(pt), surface_color, pt)
+ else:
+ self.assertEqual(surface.get_at(pt), surface_color, pt)
+
+ surface.unlock()
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever fully supports drawing aalines.
+# class PythonDrawAALinesTest(AALinesMixin, PythonDrawTestCase):
+# """Test draw_py module function aalines.
+#
+# This class inherits the general tests from AALinesMixin. It is also the
+# class to add any draw_py.draw_aalines specific tests to.
+# """
+
+
+class DrawAALinesTest(AALinesMixin, DrawTestCase):
+ """Test draw module function aalines.
+
+ This class inherits the general tests from AALinesMixin. It is also the
+ class to add any draw.aalines specific tests to.
+ """
+
+
+### Polygon Testing ###########################################################
+
+SQUARE = ([0, 0], [3, 0], [3, 3], [0, 3])
+DIAMOND = [(1, 3), (3, 5), (5, 3), (3, 1)]
+CROSS = (
+ [2, 0],
+ [4, 0],
+ [4, 2],
+ [6, 2],
+ [6, 4],
+ [4, 4],
+ [4, 6],
+ [2, 6],
+ [2, 4],
+ [0, 4],
+ [0, 2],
+ [2, 2],
+)
+
+
+class DrawPolygonMixin:
+ """Mixin tests for drawing polygons.
+
+ This class contains all the general polygon drawing tests.
+ """
+
+ def setUp(self):
+ self.surface = pygame.Surface((20, 20))
+
+ def test_polygon__args(self):
+ """Ensures draw polygon accepts the correct args."""
+ bounds_rect = self.draw_polygon(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), ((0, 0), (1, 1), (2, 2)), 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__args_without_width(self):
+ """Ensures draw polygon accepts the args without a width."""
+ bounds_rect = self.draw_polygon(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), ((0, 0), (1, 1), (2, 2))
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__kwargs(self):
+ """Ensures draw polygon accepts the correct kwargs
+ with and without a width arg.
+ """
+ surface = pygame.Surface((4, 4))
+ color = pygame.Color("yellow")
+ points = ((0, 0), (1, 1), (2, 2))
+ kwargs_list = [
+ {"surface": surface, "color": color, "points": points, "width": 1},
+ {"surface": surface, "color": color, "points": points},
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__kwargs_order_independent(self):
+ """Ensures draw polygon's kwargs are not order dependent."""
+ bounds_rect = self.draw_polygon(
+ color=(10, 20, 30),
+ surface=pygame.Surface((3, 2)),
+ width=0,
+ points=((0, 1), (1, 2), (2, 3)),
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__args_missing(self):
+ """Ensures draw polygon detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+ color = pygame.Color("blue")
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon(surface, color)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon()
+
+ def test_polygon__kwargs_missing(self):
+ """Ensures draw polygon detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((1, 2)),
+ "color": pygame.Color("red"),
+ "points": ((2, 1), (2, 2), (2, 3)),
+ "width": 1,
+ }
+
+ for name in ("points", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon(**invalid_kwargs)
+
+ def test_polygon__arg_invalid_types(self):
+ """Ensures draw polygon detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ points = ((0, 1), (1, 2), (1, 3))
+
+ with self.assertRaises(TypeError):
+ # Invalid width.
+ bounds_rect = self.draw_polygon(surface, color, points, "1")
+
+ with self.assertRaises(TypeError):
+ # Invalid points.
+ bounds_rect = self.draw_polygon(surface, color, (1, 2, 3))
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_polygon(surface, 2.3, points)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_polygon((1, 2, 3, 4), color, points)
+
+ def test_polygon__kwarg_invalid_types(self):
+ """Ensures draw polygon detects invalid kwarg types."""
+ surface = pygame.Surface((3, 3))
+ color = pygame.Color("green")
+ points = ((0, 0), (1, 0), (2, 0))
+ width = 1
+ kwargs_list = [
+ {
+ "surface": pygame.Surface, # Invalid surface.
+ "color": color,
+ "points": points,
+ "width": width,
+ },
+ {
+ "surface": surface,
+ "color": 2.3, # Invalid color.
+ "points": points,
+ "width": width,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "points": ((1,), (1,), (1,)), # Invalid points.
+ "width": width,
+ },
+ {"surface": surface, "color": color, "points": points, "width": 1.2},
+ ] # Invalid width.
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ def test_polygon__kwarg_invalid_name(self):
+ """Ensures draw polygon detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ points = ((1, 1), (1, 2), (1, 3))
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "points": points,
+ "width": 1,
+ "invalid": 1,
+ },
+ {"surface": surface, "color": color, "points": points, "invalid": 1},
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ def test_polygon__args_and_kwargs(self):
+ """Ensures draw polygon accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 1))
+ color = (255, 255, 0, 0)
+ points = ((0, 1), (1, 2), (2, 3))
+ width = 0
+ kwargs = {"surface": surface, "color": color, "points": points, "width": width}
+
+ for name in ("surface", "color", "points", "width"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_polygon(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_polygon(surface, color, **kwargs)
+ elif "points" == name:
+ bounds_rect = self.draw_polygon(surface, color, points, **kwargs)
+ else:
+ bounds_rect = self.draw_polygon(surface, color, points, width, **kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__valid_width_values(self):
+ """Ensures draw polygon accepts different width values."""
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ color = (10, 20, 30, 255)
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "points": ((1, 1), (2, 1), (2, 2), (1, 2)),
+ "width": None,
+ }
+ pos = kwargs["points"][0]
+
+ for width in (-100, -10, -1, 0, 1, 10, 100):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["width"] = width
+ expected_color = color if width >= 0 else surface_color
+
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__valid_points_format(self):
+ """Ensures draw polygon accepts different points formats."""
+ expected_color = (10, 20, 30, 255)
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "points": None,
+ "width": 0,
+ }
+
+ # The point type can be a tuple/list/Vector2.
+ point_types = (
+ (tuple, tuple, tuple, tuple), # all tuples
+ (list, list, list, list), # all lists
+ (Vector2, Vector2, Vector2, Vector2), # all Vector2s
+ (list, Vector2, tuple, Vector2),
+ ) # mix
+
+ # The point values can be ints or floats.
+ point_values = (
+ ((1, 1), (2, 1), (2, 2), (1, 2)),
+ ((1, 1), (2.2, 1), (2.1, 2.2), (1, 2.1)),
+ )
+
+ # Each sequence of points can be a tuple or a list.
+ seq_types = (tuple, list)
+
+ for point_type in point_types:
+ for values in point_values:
+ check_pos = values[0]
+ points = [point_type[i](pt) for i, pt in enumerate(values)]
+
+ for seq_type in seq_types:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["points"] = seq_type(points)
+
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ self.assertEqual(surface.get_at(check_pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__invalid_points_formats(self):
+ """Ensures draw polygon handles invalid points formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "points": None,
+ "width": 0,
+ }
+
+ points_fmts = (
+ ((1, 1), (2, 1), (2,)), # Too few coords.
+ ((1, 1), (2, 1), (2, 2, 2)), # Too many coords.
+ ((1, 1), (2, 1), (2, "2")), # Wrong type.
+ ((1, 1), (2, 1), {2, 3}), # Wrong type.
+ ((1, 1), (2, 1), {2: 2, 3: 3}), # Wrong type.
+ {(1, 1), (2, 1), (2, 2), (1, 2)}, # Wrong type.
+ {1: 1, 2: 2, 3: 3, 4: 4},
+ ) # Wrong type.
+
+ for points in points_fmts:
+ kwargs["points"] = points
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ def test_polygon__invalid_points_values(self):
+ """Ensures draw polygon handles invalid points values correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "points": None,
+ "width": 0,
+ }
+
+ points_fmts = (
+ (), # Too few points.
+ ((1, 1),), # Too few points.
+ ((1, 1), (2, 1)),
+ ) # Too few points.
+
+ for points in points_fmts:
+ for seq_type in (tuple, list): # Test as tuples and lists.
+ kwargs["points"] = seq_type(points)
+
+ with self.assertRaises(ValueError):
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ def test_polygon__valid_color_formats(self):
+ """Ensures draw polygon accepts different color formats."""
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "points": ((1, 1), (2, 1), (2, 2), (1, 2)),
+ "width": 0,
+ }
+ pos = kwargs["points"][0]
+ greens = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in greens:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_polygon__invalid_color_formats(self):
+ """Ensures draw polygon handles invalid color formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 3)),
+ "color": None,
+ "points": ((1, 1), (2, 1), (2, 2), (1, 2)),
+ "width": 0,
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_polygon(**kwargs)
+
+ def test_draw_square(self):
+ self.draw_polygon(self.surface, RED, SQUARE, 0)
+ # note : there is a discussion (#234) if draw.polygon should include or
+ # not the right or lower border; here we stick with current behavior,
+ # eg include those borders ...
+ for x in range(4):
+ for y in range(4):
+ self.assertEqual(self.surface.get_at((x, y)), RED)
+
+ def test_draw_diamond(self):
+ pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0)
+ self.draw_polygon(self.surface, GREEN, DIAMOND, 0)
+ # this diamond shape is equivalent to its four corners, plus inner square
+ for x, y in DIAMOND:
+ self.assertEqual(self.surface.get_at((x, y)), GREEN, msg=str((x, y)))
+ for x in range(2, 5):
+ for y in range(2, 5):
+ self.assertEqual(self.surface.get_at((x, y)), GREEN)
+
+ def test_1_pixel_high_or_wide_shapes(self):
+ # 1. one-pixel-high, filled
+ pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0)
+ self.draw_polygon(self.surface, GREEN, [(x, 2) for x, _y in CROSS], 0)
+ cross_size = 6 # the maximum x or y coordinate of the cross
+ for x in range(cross_size + 1):
+ self.assertEqual(self.surface.get_at((x, 1)), RED)
+ self.assertEqual(self.surface.get_at((x, 2)), GREEN)
+ self.assertEqual(self.surface.get_at((x, 3)), RED)
+ pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0)
+ # 2. one-pixel-high, not filled
+ self.draw_polygon(self.surface, GREEN, [(x, 5) for x, _y in CROSS], 1)
+ for x in range(cross_size + 1):
+ self.assertEqual(self.surface.get_at((x, 4)), RED)
+ self.assertEqual(self.surface.get_at((x, 5)), GREEN)
+ self.assertEqual(self.surface.get_at((x, 6)), RED)
+ pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0)
+ # 3. one-pixel-wide, filled
+ self.draw_polygon(self.surface, GREEN, [(3, y) for _x, y in CROSS], 0)
+ for y in range(cross_size + 1):
+ self.assertEqual(self.surface.get_at((2, y)), RED)
+ self.assertEqual(self.surface.get_at((3, y)), GREEN)
+ self.assertEqual(self.surface.get_at((4, y)), RED)
+ pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0)
+ # 4. one-pixel-wide, not filled
+ self.draw_polygon(self.surface, GREEN, [(4, y) for _x, y in CROSS], 1)
+ for y in range(cross_size + 1):
+ self.assertEqual(self.surface.get_at((3, y)), RED)
+ self.assertEqual(self.surface.get_at((4, y)), GREEN)
+ self.assertEqual(self.surface.get_at((5, y)), RED)
+
+ def test_draw_symetric_cross(self):
+ """non-regression on issue #234 : x and y where handled inconsistently.
+
+ Also, the result is/was different whether we fill or not the polygon.
+ """
+ # 1. case width = 1 (not filled: `polygon` calls internally the `lines` function)
+ pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0)
+ self.draw_polygon(self.surface, GREEN, CROSS, 1)
+ inside = [(x, 3) for x in range(1, 6)] + [(3, y) for y in range(1, 6)]
+ for x in range(10):
+ for y in range(10):
+ if (x, y) in inside:
+ self.assertEqual(self.surface.get_at((x, y)), RED)
+ elif (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7):
+ # we are on the border of the cross:
+ self.assertEqual(self.surface.get_at((x, y)), GREEN)
+ else:
+ # we are outside
+ self.assertEqual(self.surface.get_at((x, y)), RED)
+
+ # 2. case width = 0 (filled; this is the example from #234)
+ pygame.draw.rect(self.surface, RED, (0, 0, 10, 10), 0)
+ self.draw_polygon(self.surface, GREEN, CROSS, 0)
+ inside = [(x, 3) for x in range(1, 6)] + [(3, y) for y in range(1, 6)]
+ for x in range(10):
+ for y in range(10):
+ if (x in range(2, 5) and y < 7) or (y in range(2, 5) and x < 7):
+ # we are on the border of the cross:
+ self.assertEqual(
+ self.surface.get_at((x, y)), GREEN, msg=str((x, y))
+ )
+ else:
+ # we are outside
+ self.assertEqual(self.surface.get_at((x, y)), RED)
+
+ def test_illumine_shape(self):
+ """non-regression on issue #313"""
+ rect = pygame.Rect((0, 0, 20, 20))
+ path_data = [
+ (0, 0),
+ (rect.width - 1, 0), # upper border
+ (rect.width - 5, 5 - 1),
+ (5 - 1, 5 - 1), # upper inner
+ (5 - 1, rect.height - 5),
+ (0, rect.height - 1),
+ ] # lower diagonal
+ # The shape looks like this (the numbers are the indices of path_data)
+
+ # 0**********************1 <-- upper border
+ # ***********************
+ # **********************
+ # *********************
+ # ****3**************2 <-- upper inner border
+ # *****
+ # ***** (more lines here)
+ # *****
+ # ****4
+ # ****
+ # ***
+ # **
+ # 5
+ #
+
+ # the current bug is that the "upper inner" line is not drawn, but only
+ # if 4 or some lower corner exists
+ pygame.draw.rect(self.surface, RED, (0, 0, 20, 20), 0)
+
+ # 1. First without the corners 4 & 5
+ self.draw_polygon(self.surface, GREEN, path_data[:4], 0)
+ for x in range(20):
+ self.assertEqual(self.surface.get_at((x, 0)), GREEN) # upper border
+ for x in range(4, rect.width - 5 + 1):
+ self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner
+
+ # 2. with the corners 4 & 5
+ pygame.draw.rect(self.surface, RED, (0, 0, 20, 20), 0)
+ self.draw_polygon(self.surface, GREEN, path_data, 0)
+ for x in range(4, rect.width - 5 + 1):
+ self.assertEqual(self.surface.get_at((x, 4)), GREEN) # upper inner
+
+ def test_invalid_points(self):
+ self.assertRaises(
+ TypeError,
+ lambda: self.draw_polygon(
+ self.surface, RED, ((0, 0), (0, 20), (20, 20), 20), 0
+ ),
+ )
+
+ def test_polygon__bounding_rect(self):
+ """Ensures draw polygon returns the correct bounding rect.
+
+ Tests polygons on and off the surface and a range of width/thickness
+ values.
+ """
+ polygon_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ min_width = min_height = 5
+ max_width = max_height = 7
+ sizes = ((min_width, min_height), (max_width, max_height))
+ surface = pygame.Surface((20, 20), 0, 32)
+ surf_rect = surface.get_rect()
+ # Make a rect that is bigger than the surface to help test drawing
+ # polygons off and partially off the surface.
+ big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1)
+
+ for pos in rect_corners_mids_and_center(
+ surf_rect
+ ) + rect_corners_mids_and_center(big_rect):
+ # A rect (pos_rect) is used to help create and position the
+ # polygon. Each of this rect's position attributes will be set to
+ # the pos value.
+ for attr in RECT_POSITION_ATTRIBUTES:
+ # Test using different rect sizes and thickness values.
+ for width, height in sizes:
+ pos_rect = pygame.Rect((0, 0), (width, height))
+ setattr(pos_rect, attr, pos)
+ # Points form a triangle with no fully
+ # horizontal/vertical lines.
+ vertices = (
+ pos_rect.midleft,
+ pos_rect.midtop,
+ pos_rect.bottomright,
+ )
+
+ for thickness in range(4):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_polygon(
+ surface, polygon_color, vertices, thickness
+ )
+
+ # Calculating the expected_rect after the polygon
+ # is drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(
+ surface, surf_color, vertices[0]
+ )
+
+ self.assertEqual(
+ bounding_rect,
+ expected_rect,
+ f"thickness={thickness}",
+ )
+
+ def test_polygon__surface_clip(self):
+ """Ensures draw polygon respects a surface's clip area.
+
+ Tests drawing the polygon filled and unfilled.
+ """
+ surfw = surfh = 30
+ polygon_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (8, 10))
+ clip_rect.center = surface.get_rect().center
+ pos_rect = clip_rect.copy() # Manages the polygon's pos.
+
+ for width in (0, 1): # Filled and unfilled.
+ # Test centering the polygon along the clip rect's edge.
+ for center in rect_corners_mids_and_center(clip_rect):
+ # Get the expected points by drawing the polygon without the
+ # clip area set.
+ pos_rect.center = center
+ vertices = (
+ pos_rect.topleft,
+ pos_rect.topright,
+ pos_rect.bottomright,
+ pos_rect.bottomleft,
+ )
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_polygon(surface, polygon_color, vertices, width)
+ expected_pts = get_color_points(surface, polygon_color, clip_rect)
+
+ # Clear the surface and set the clip area. Redraw the polygon
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_polygon(surface, polygon_color, vertices, width)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure only the expected_pts
+ # are the polygon_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ expected_color = polygon_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(surface.get_at(pt), expected_color, pt)
+
+ surface.unlock()
+
+ # Test cases for Issue #3989 (fixed on #4191)
+ # This tests the fill polygon bug to avoid
+ def test_polygon_large_coords_3989(self):
+ """
+ Ensures draw polygon works correctly with large points.
+ Testing the drawings of filled polygons
+ """
+ point_a = (600, 50)
+ point_b = (50, 600)
+ extreme_points_coords = (58000, 100000)
+ extreme_negative_coords = (-58000, -100000)
+ extreme_negative_x = (-58000, 100000)
+ extreme_negative_y = (58000, -100000)
+
+ surf_w = surf_h = 650
+ surface = pygame.Surface((surf_w, surf_h))
+
+ green = (0, 255, 0)
+ white = (0, 0, 0, 255)
+
+ # Draw white background
+ pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+
+ # Extreme points case
+ def extreme_points(self):
+ self.assertEqual(surface.get_at((640, 50)), white)
+ self.assertEqual(surface.get_at((50, 640)), white)
+
+ # Extreme negative points case
+ def extreme_negative_pass(self):
+ self.assertEqual(surface.get_at((600, 25)), white)
+
+ def extreme_negative_fail(self):
+ self.assertNotEqual(surface.get_at((5, 5)), white)
+
+ # Extreme negative x case
+ def extreme_x_pass(self):
+ self.assertEqual(surface.get_at((600, 600)), white)
+
+ def extreme_x_fail(self):
+ self.assertNotEqual(surface.get_at((100, 640)), white)
+
+ # Extreme negative y case
+ def extreme_y_pass(self):
+ self.assertEqual(surface.get_at((600, 600)), white)
+
+ def extreme_y_fail(self):
+ self.assertNotEqual(surface.get_at((300, 300)), white)
+
+ # Checks the surface point to ensure the polygon has been drawn correctly.
+ # Uses multiple passing and failing test cases depending on the polygon
+ self.draw_polygon(surface, green, (point_a, point_b, extreme_points_coords))
+ extreme_points(self)
+
+ pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+ self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_coords))
+ extreme_negative_pass(self)
+ extreme_negative_fail(self)
+
+ pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+ self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_x))
+ extreme_x_pass(self)
+ extreme_x_fail(self)
+
+ pygame.draw.rect(surface, white, (0, 0, surf_w, surf_h), 0)
+ self.draw_polygon(surface, green, (point_a, point_b, extreme_negative_y))
+ extreme_y_pass(self)
+ extreme_y_fail(self)
+
+
+class DrawPolygonTest(DrawPolygonMixin, DrawTestCase):
+ """Test draw module function polygon.
+
+ This class inherits the general tests from DrawPolygonMixin. It is also
+ the class to add any draw.polygon specific tests to.
+ """
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever fully supports drawing polygons.
+# @unittest.skip('draw_py.draw_polygon not fully supported yet')
+# class PythonDrawPolygonTest(DrawPolygonMixin, PythonDrawTestCase):
+# """Test draw_py module function draw_polygon.
+#
+# This class inherits the general tests from DrawPolygonMixin. It is also
+# the class to add any draw_py.draw_polygon specific tests to.
+# """
+
+
+### Rect Testing ##############################################################
+
+
+class DrawRectMixin:
+ """Mixin tests for drawing rects.
+
+ This class contains all the general rect drawing tests.
+ """
+
+ def test_rect__args(self):
+ """Ensures draw rect accepts the correct args."""
+ bounds_rect = self.draw_rect(
+ pygame.Surface((2, 2)),
+ (20, 10, 20, 150),
+ pygame.Rect((0, 0), (1, 1)),
+ 2,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__args_without_width(self):
+ """Ensures draw rect accepts the args without a width and borders."""
+ bounds_rect = self.draw_rect(
+ pygame.Surface((3, 5)), (0, 0, 0, 255), pygame.Rect((0, 0), (1, 1))
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__kwargs(self):
+ """Ensures draw rect accepts the correct kwargs
+ with and without a width and border_radius arg.
+ """
+ kwargs_list = [
+ {
+ "surface": pygame.Surface((5, 5)),
+ "color": pygame.Color("red"),
+ "rect": pygame.Rect((0, 0), (1, 2)),
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": pygame.Surface((1, 2)),
+ "color": (0, 100, 200),
+ "rect": (0, 0, 1, 1),
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_rect(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__kwargs_order_independent(self):
+ """Ensures draw rect's kwargs are not order dependent."""
+ bounds_rect = self.draw_rect(
+ color=(0, 1, 2),
+ border_radius=10,
+ surface=pygame.Surface((2, 3)),
+ border_top_left_radius=5,
+ width=-2,
+ border_top_right_radius=20,
+ border_bottom_right_radius=0,
+ rect=pygame.Rect((0, 0), (0, 0)),
+ border_bottom_left_radius=15,
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__args_missing(self):
+ """Ensures draw rect detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect(surface, pygame.Color("white"))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect()
+
+ def test_rect__kwargs_missing(self):
+ """Ensures draw rect detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((1, 3)),
+ "color": pygame.Color("red"),
+ "rect": pygame.Rect((0, 0), (2, 2)),
+ "width": 5,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ }
+
+ for name in ("rect", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect(**invalid_kwargs)
+
+ def test_rect__arg_invalid_types(self):
+ """Ensures draw rect detects invalid arg types."""
+ surface = pygame.Surface((3, 3))
+ color = pygame.Color("white")
+ rect = pygame.Rect((1, 1), (1, 1))
+
+ with self.assertRaises(TypeError):
+ # Invalid border_bottom_right_radius.
+ bounds_rect = self.draw_rect(
+ surface, color, rect, 2, border_bottom_right_radius="rad"
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid border_bottom_left_radius.
+ bounds_rect = self.draw_rect(
+ surface, color, rect, 2, border_bottom_left_radius="rad"
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid border_top_right_radius.
+ bounds_rect = self.draw_rect(
+ surface, color, rect, 2, border_top_right_radius="rad"
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid border_top_left_radius.
+ bounds_rect = self.draw_rect(
+ surface, color, rect, 2, border_top_left_radius="draw"
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid border_radius.
+ bounds_rect = self.draw_rect(surface, color, rect, 2, "rad")
+
+ with self.assertRaises(TypeError):
+ # Invalid width.
+ bounds_rect = self.draw_rect(surface, color, rect, "2", 4)
+
+ with self.assertRaises(TypeError):
+ # Invalid rect.
+ bounds_rect = self.draw_rect(surface, color, (1, 2, 3), 2, 6)
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_rect(surface, 2.3, rect, 3, 8)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_rect(rect, color, rect, 4, 10)
+
+ def test_rect__kwarg_invalid_types(self):
+ """Ensures draw rect detects invalid kwarg types."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("red")
+ rect = pygame.Rect((0, 0), (1, 1))
+ kwargs_list = [
+ {
+ "surface": pygame.Surface, # Invalid surface.
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": 2.3, # Invalid color.
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": (1, 1, 2), # Invalid rect.
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1.1, # Invalid width.
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10.5, # Invalid border_radius.
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5.5, # Invalid top_left_radius.
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": "a", # Invalid top_right_radius.
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": "c", # Invalid bottom_left_radius
+ "border_bottom_right_radius": 0,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": "d", # Invalid bottom_right.
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect(**kwargs)
+
+ def test_rect__kwarg_invalid_name(self):
+ """Ensures draw rect detects invalid kwarg names."""
+ surface = pygame.Surface((2, 1))
+ color = pygame.Color("green")
+ rect = pygame.Rect((0, 0), (3, 3))
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "width": 1,
+ "border_radius": 10,
+ "border_top_left_radius": 5,
+ "border_top_right_radius": 20,
+ "border_bottom_left_radius": 15,
+ "border_bottom_right_radius": 0,
+ "invalid": 1,
+ },
+ {"surface": surface, "color": color, "rect": rect, "invalid": 1},
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect(**kwargs)
+
+ def test_rect__args_and_kwargs(self):
+ """Ensures draw rect accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 1))
+ color = (255, 255, 255, 0)
+ rect = pygame.Rect((1, 0), (2, 5))
+ width = 0
+ kwargs = {"surface": surface, "color": color, "rect": rect, "width": width}
+
+ for name in ("surface", "color", "rect", "width"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_rect(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_rect(surface, color, **kwargs)
+ elif "rect" == name:
+ bounds_rect = self.draw_rect(surface, color, rect, **kwargs)
+ else:
+ bounds_rect = self.draw_rect(surface, color, rect, width, **kwargs)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__valid_width_values(self):
+ """Ensures draw rect accepts different width values."""
+ pos = (1, 1)
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ color = (1, 2, 3, 255)
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "rect": pygame.Rect(pos, (2, 2)),
+ "width": None,
+ }
+
+ for width in (-1000, -10, -1, 0, 1, 10, 1000):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["width"] = width
+ expected_color = color if width >= 0 else surface_color
+
+ bounds_rect = self.draw_rect(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__valid_rect_formats(self):
+ """Ensures draw rect accepts different rect formats."""
+ pos = (1, 1)
+ expected_color = pygame.Color("yellow")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ kwargs = {"surface": surface, "color": expected_color, "rect": None, "width": 0}
+ rects = (
+ pygame.Rect(pos, (1, 1)),
+ (pos, (2, 2)),
+ (pos[0], pos[1], 3, 3),
+ [pos, (2.1, 2.2)],
+ )
+
+ for rect in rects:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["rect"] = rect
+
+ bounds_rect = self.draw_rect(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__invalid_rect_formats(self):
+ """Ensures draw rect handles invalid rect formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("red"),
+ "rect": None,
+ "width": 0,
+ }
+
+ invalid_fmts = (
+ [],
+ [1],
+ [1, 2],
+ [1, 2, 3],
+ [1, 2, 3, 4, 5],
+ {1, 2, 3, 4},
+ [1, 2, 3, "4"],
+ )
+
+ for rect in invalid_fmts:
+ kwargs["rect"] = rect
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect(**kwargs)
+
+ def test_rect__valid_color_formats(self):
+ """Ensures draw rect accepts different color formats."""
+ pos = (1, 1)
+ red_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "rect": pygame.Rect(pos, (1, 1)),
+ "width": 3,
+ }
+ reds = ((255, 0, 0), (255, 0, 0, 255), surface.map_rgb(red_color), red_color)
+
+ for color in reds:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = red_color
+
+ bounds_rect = self.draw_rect(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_rect__invalid_color_formats(self):
+ """Ensures draw rect handles invalid color formats correctly."""
+ pos = (1, 1)
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "rect": pygame.Rect(pos, (1, 1)),
+ "width": 1,
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_rect(**kwargs)
+
+ def test_rect__fill(self):
+ self.surf_w, self.surf_h = self.surf_size = (320, 200)
+ self.surf = pygame.Surface(self.surf_size, pygame.SRCALPHA)
+ self.color = (1, 13, 24, 205)
+ rect = pygame.Rect(10, 10, 25, 20)
+ drawn = self.draw_rect(self.surf, self.color, rect, 0)
+
+ self.assertEqual(drawn, rect)
+
+ # Should be colored where it's supposed to be
+ for pt in test_utils.rect_area_pts(rect):
+ color_at_pt = self.surf.get_at(pt)
+
+ self.assertEqual(color_at_pt, self.color)
+
+ # And not where it shouldn't
+ for pt in test_utils.rect_outer_bounds(rect):
+ color_at_pt = self.surf.get_at(pt)
+
+ self.assertNotEqual(color_at_pt, self.color)
+
+ # Issue #310: Cannot draw rectangles that are 1 pixel high
+ bgcolor = pygame.Color("black")
+ self.surf.fill(bgcolor)
+ hrect = pygame.Rect(1, 1, self.surf_w - 2, 1)
+ vrect = pygame.Rect(1, 3, 1, self.surf_h - 4)
+
+ drawn = self.draw_rect(self.surf, self.color, hrect, 0)
+
+ self.assertEqual(drawn, hrect)
+
+ x, y = hrect.topleft
+ w, h = hrect.size
+
+ self.assertEqual(self.surf.get_at((x - 1, y)), bgcolor)
+ self.assertEqual(self.surf.get_at((x + w, y)), bgcolor)
+ for i in range(x, x + w):
+ self.assertEqual(self.surf.get_at((i, y)), self.color)
+
+ drawn = self.draw_rect(self.surf, self.color, vrect, 0)
+
+ self.assertEqual(drawn, vrect)
+
+ x, y = vrect.topleft
+ w, h = vrect.size
+
+ self.assertEqual(self.surf.get_at((x, y - 1)), bgcolor)
+ self.assertEqual(self.surf.get_at((x, y + h)), bgcolor)
+ for i in range(y, y + h):
+ self.assertEqual(self.surf.get_at((x, i)), self.color)
+
+ def test_rect__one_pixel_lines(self):
+ self.surf = pygame.Surface((320, 200), pygame.SRCALPHA)
+ self.color = (1, 13, 24, 205)
+
+ rect = pygame.Rect(10, 10, 56, 20)
+
+ drawn = self.draw_rect(self.surf, self.color, rect, 1)
+
+ self.assertEqual(drawn, rect)
+
+ # Should be colored where it's supposed to be
+ for pt in test_utils.rect_perimeter_pts(drawn):
+ color_at_pt = self.surf.get_at(pt)
+
+ self.assertEqual(color_at_pt, self.color)
+
+ # And not where it shouldn't
+ for pt in test_utils.rect_outer_bounds(drawn):
+ color_at_pt = self.surf.get_at(pt)
+
+ self.assertNotEqual(color_at_pt, self.color)
+
+ def test_rect__draw_line_width(self):
+ surface = pygame.Surface((100, 100))
+ surface.fill("black")
+ color = pygame.Color(255, 255, 255)
+ rect_width = 80
+ rect_height = 50
+ line_width = 10
+ pygame.draw.rect(
+ surface, color, pygame.Rect(0, 0, rect_width, rect_height), line_width
+ )
+ for i in range(line_width):
+ self.assertEqual(surface.get_at((i, i)), color)
+ self.assertEqual(surface.get_at((rect_width - i - 1, i)), color)
+ self.assertEqual(surface.get_at((i, rect_height - i - 1)), color)
+ self.assertEqual(
+ surface.get_at((rect_width - i - 1, rect_height - i - 1)), color
+ )
+ self.assertEqual(surface.get_at((line_width, line_width)), (0, 0, 0))
+ self.assertEqual(
+ surface.get_at((rect_width - line_width - 1, line_width)), (0, 0, 0)
+ )
+ self.assertEqual(
+ surface.get_at((line_width, rect_height - line_width - 1)), (0, 0, 0)
+ )
+ self.assertEqual(
+ surface.get_at((rect_width - line_width - 1, rect_height - line_width - 1)),
+ (0, 0, 0),
+ )
+
+ def test_rect__bounding_rect(self):
+ """Ensures draw rect returns the correct bounding rect.
+
+ Tests rects on and off the surface and a range of width/thickness
+ values.
+ """
+ rect_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ min_width = min_height = 5
+ max_width = max_height = 7
+ sizes = ((min_width, min_height), (max_width, max_height))
+ surface = pygame.Surface((20, 20), 0, 32)
+ surf_rect = surface.get_rect()
+ # Make a rect that is bigger than the surface to help test drawing
+ # rects off and partially off the surface.
+ big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1)
+
+ for pos in rect_corners_mids_and_center(
+ surf_rect
+ ) + rect_corners_mids_and_center(big_rect):
+ # Each of the rect's position attributes will be set to the pos
+ # value.
+ for attr in RECT_POSITION_ATTRIBUTES:
+ # Test using different rect sizes and thickness values.
+ for width, height in sizes:
+ rect = pygame.Rect((0, 0), (width, height))
+ setattr(rect, attr, pos)
+
+ for thickness in range(4):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_rect(
+ surface, rect_color, rect, thickness
+ )
+
+ # Calculating the expected_rect after the rect is
+ # drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(
+ surface, surf_color, rect.topleft
+ )
+
+ self.assertEqual(
+ bounding_rect,
+ expected_rect,
+ f"thickness={thickness}",
+ )
+
+ def test_rect__surface_clip(self):
+ """Ensures draw rect respects a surface's clip area.
+
+ Tests drawing the rect filled and unfilled.
+ """
+ surfw = surfh = 30
+ rect_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (8, 10))
+ clip_rect.center = surface.get_rect().center
+ test_rect = clip_rect.copy() # Manages the rect's pos.
+
+ for width in (0, 1): # Filled and unfilled.
+ # Test centering the rect along the clip rect's edge.
+ for center in rect_corners_mids_and_center(clip_rect):
+ # Get the expected points by drawing the rect without the
+ # clip area set.
+ test_rect.center = center
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_rect(surface, rect_color, test_rect, width)
+ expected_pts = get_color_points(surface, rect_color, clip_rect)
+
+ # Clear the surface and set the clip area. Redraw the rect
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_rect(surface, rect_color, test_rect, width)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure only the expected_pts
+ # are the rect_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ expected_color = rect_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(surface.get_at(pt), expected_color, pt)
+
+ surface.unlock()
+
+
+class DrawRectTest(DrawRectMixin, DrawTestCase):
+ """Test draw module function rect.
+
+ This class inherits the general tests from DrawRectMixin. It is also the
+ class to add any draw.rect specific tests to.
+ """
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever properly supports drawing rects.
+# @unittest.skip('draw_py.draw_rect not supported yet')
+# class PythonDrawRectTest(DrawRectMixin, PythonDrawTestCase):
+# """Test draw_py module function draw_rect.
+#
+# This class inherits the general tests from DrawRectMixin. It is also the
+# class to add any draw_py.draw_rect specific tests to.
+# """
+
+
+### Circle Testing ############################################################
+
+
+class DrawCircleMixin:
+ """Mixin tests for drawing circles.
+
+ This class contains all the general circle drawing tests.
+ """
+
+ def test_circle__args(self):
+ """Ensures draw circle accepts the correct args."""
+ bounds_rect = self.draw_circle(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), (0, 0), 3, 1, 1, 0, 1, 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__args_without_width(self):
+ """Ensures draw circle accepts the args without a width and
+ quadrants."""
+ bounds_rect = self.draw_circle(pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 1)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__args_with_negative_width(self):
+ """Ensures draw circle accepts the args with negative width."""
+ bounds_rect = self.draw_circle(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 1, -1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+ self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0))
+
+ def test_circle__args_with_width_gt_radius(self):
+ """Ensures draw circle accepts the args with width > radius."""
+ bounds_rect = self.draw_circle(
+ pygame.Surface((2, 2)), (0, 0, 0, 50), (1, 1), 2, 3, 0, 0, 0, 0
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+ self.assertEqual(bounds_rect, pygame.Rect(0, 0, 2, 2))
+
+ def test_circle__kwargs(self):
+ """Ensures draw circle accepts the correct kwargs
+ with and without a width and quadrant arguments.
+ """
+ kwargs_list = [
+ {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("yellow"),
+ "center": (2, 2),
+ "radius": 2,
+ "width": 1,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": False,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": pygame.Surface((2, 1)),
+ "color": (0, 10, 20),
+ "center": (1, 1),
+ "radius": 1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_circle(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__kwargs_order_independent(self):
+ """Ensures draw circle's kwargs are not order dependent."""
+ bounds_rect = self.draw_circle(
+ draw_top_right=False,
+ color=(10, 20, 30),
+ surface=pygame.Surface((3, 2)),
+ width=0,
+ draw_bottom_left=False,
+ center=(1, 0),
+ draw_bottom_right=False,
+ radius=2,
+ draw_top_left=True,
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__args_missing(self):
+ """Ensures draw circle detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+ color = pygame.Color("blue")
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle(surface, color, (0, 0))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle(surface, color)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle()
+
+ def test_circle__kwargs_missing(self):
+ """Ensures draw circle detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((1, 2)),
+ "color": pygame.Color("red"),
+ "center": (1, 0),
+ "radius": 2,
+ "width": 1,
+ "draw_top_right": False,
+ "draw_top_left": False,
+ "draw_bottom_left": False,
+ "draw_bottom_right": True,
+ }
+
+ for name in ("radius", "center", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle(**invalid_kwargs)
+
+ def test_circle__arg_invalid_types(self):
+ """Ensures draw circle detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ center = (1, 1)
+ radius = 1
+
+ with self.assertRaises(TypeError):
+ # Invalid draw_top_right.
+ bounds_rect = self.draw_circle(
+ surface, color, center, radius, 1, "a", 1, 1, 1
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid draw_top_left.
+ bounds_rect = self.draw_circle(
+ surface, color, center, radius, 1, 1, "b", 1, 1
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid draw_bottom_left.
+ bounds_rect = self.draw_circle(
+ surface, color, center, radius, 1, 1, 1, "c", 1
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid draw_bottom_right.
+ bounds_rect = self.draw_circle(
+ surface, color, center, radius, 1, 1, 1, 1, "d"
+ )
+
+ with self.assertRaises(TypeError):
+ # Invalid width.
+ bounds_rect = self.draw_circle(surface, color, center, radius, "1")
+
+ with self.assertRaises(TypeError):
+ # Invalid radius.
+ bounds_rect = self.draw_circle(surface, color, center, "2")
+
+ with self.assertRaises(TypeError):
+ # Invalid center.
+ bounds_rect = self.draw_circle(surface, color, (1, 2, 3), radius)
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_circle(surface, 2.3, center, radius)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_circle((1, 2, 3, 4), color, center, radius)
+
+ def test_circle__kwarg_invalid_types(self):
+ """Ensures draw circle detects invalid kwarg types."""
+ surface = pygame.Surface((3, 3))
+ color = pygame.Color("green")
+ center = (0, 1)
+ radius = 1
+ width = 1
+ quadrant = 1
+ kwargs_list = [
+ {
+ "surface": pygame.Surface, # Invalid surface.
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": 2.3, # Invalid color.
+ "center": center,
+ "radius": radius,
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": (1, 1, 1), # Invalid center.
+ "radius": radius,
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": "1", # Invalid radius.
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": 1.2, # Invalid width.
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": width,
+ "draw_top_right": "True", # Invalid draw_top_right
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": "True", # Invalid draw_top_left
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": 3.14, # Invalid draw_bottom_left
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": "quadrant", # Invalid draw_bottom_right
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle(**kwargs)
+
+ def test_circle__kwarg_invalid_name(self):
+ """Ensures draw circle detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ center = (0, 0)
+ radius = 2
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": 1,
+ "quadrant": 1,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "invalid": 1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle(**kwargs)
+
+ def test_circle__args_and_kwargs(self):
+ """Ensures draw circle accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 1))
+ color = (255, 255, 0, 0)
+ center = (1, 0)
+ radius = 2
+ width = 0
+ draw_top_right = True
+ draw_top_left = False
+ draw_bottom_left = False
+ draw_bottom_right = True
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": width,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ }
+
+ for name in (
+ "surface",
+ "color",
+ "center",
+ "radius",
+ "width",
+ "draw_top_right",
+ "draw_top_left",
+ "draw_bottom_left",
+ "draw_bottom_right",
+ ):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_circle(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_circle(surface, color, **kwargs)
+ elif "center" == name:
+ bounds_rect = self.draw_circle(surface, color, center, **kwargs)
+ elif "radius" == name:
+ bounds_rect = self.draw_circle(surface, color, center, radius, **kwargs)
+ elif "width" == name:
+ bounds_rect = self.draw_circle(
+ surface, color, center, radius, width, **kwargs
+ )
+ elif "draw_top_right" == name:
+ bounds_rect = self.draw_circle(
+ surface, color, center, radius, width, draw_top_right, **kwargs
+ )
+ elif "draw_top_left" == name:
+ bounds_rect = self.draw_circle(
+ surface,
+ color,
+ center,
+ radius,
+ width,
+ draw_top_right,
+ draw_top_left,
+ **kwargs,
+ )
+ elif "draw_bottom_left" == name:
+ bounds_rect = self.draw_circle(
+ surface,
+ color,
+ center,
+ radius,
+ width,
+ draw_top_right,
+ draw_top_left,
+ draw_bottom_left,
+ **kwargs,
+ )
+ else:
+ bounds_rect = self.draw_circle(
+ surface,
+ color,
+ center,
+ radius,
+ width,
+ draw_top_right,
+ draw_top_left,
+ draw_bottom_left,
+ draw_bottom_right,
+ **kwargs,
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__valid_width_values(self):
+ """Ensures draw circle accepts different width values."""
+ center = (2, 2)
+ radius = 1
+ pos = (center[0] - radius, center[1])
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ color = (10, 20, 30, 255)
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": radius,
+ "width": None,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ }
+
+ for width in (-100, -10, -1, 0, 1, 10, 100):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["width"] = width
+ expected_color = color if width >= 0 else surface_color
+
+ bounds_rect = self.draw_circle(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__valid_radius_values(self):
+ """Ensures draw circle accepts different radius values."""
+ pos = center = (2, 2)
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((3, 4))
+ color = (10, 20, 30, 255)
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "center": center,
+ "radius": None,
+ "width": 0,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ }
+
+ for radius in (-10, -1, 0, 1, 10):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["radius"] = radius
+ expected_color = color if radius > 0 else surface_color
+
+ bounds_rect = self.draw_circle(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__valid_center_formats(self):
+ """Ensures draw circle accepts different center formats."""
+ expected_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((4, 4))
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "center": None,
+ "radius": 1,
+ "width": 0,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ }
+ x, y = 2, 2 # center position
+
+ # The center values can be ints or floats.
+ for center in ((x, y), (x + 0.1, y), (x, y + 0.1), (x + 0.1, y + 0.1)):
+ # The center type can be a tuple/list/Vector2.
+ for seq_type in (tuple, list, Vector2):
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["center"] = seq_type(center)
+
+ bounds_rect = self.draw_circle(**kwargs)
+
+ self.assertEqual(surface.get_at((x, y)), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__valid_color_formats(self):
+ """Ensures draw circle accepts different color formats."""
+ center = (2, 2)
+ radius = 1
+ pos = (center[0] - radius, center[1])
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((3, 4))
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "center": center,
+ "radius": radius,
+ "width": 0,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ }
+ greens = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in greens:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_circle(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_circle__invalid_color_formats(self):
+ """Ensures draw circle handles invalid color formats correctly."""
+ kwargs = {
+ "surface": pygame.Surface((4, 3)),
+ "color": None,
+ "center": (1, 2),
+ "radius": 1,
+ "width": 0,
+ "draw_top_right": True,
+ "draw_top_left": True,
+ "draw_bottom_left": True,
+ "draw_bottom_right": True,
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_circle(**kwargs)
+
+ def test_circle__floats(self):
+ """Ensure that floats are accepted."""
+ draw.circle(
+ surface=pygame.Surface((4, 4)),
+ color=(255, 255, 127),
+ center=(1.5, 1.5),
+ radius=1.3,
+ width=0,
+ draw_top_right=True,
+ draw_top_left=True,
+ draw_bottom_left=True,
+ draw_bottom_right=True,
+ )
+
+ draw.circle(
+ surface=pygame.Surface((4, 4)),
+ color=(255, 255, 127),
+ center=Vector2(1.5, 1.5),
+ radius=1.3,
+ width=0,
+ draw_top_right=True,
+ draw_top_left=True,
+ draw_bottom_left=True,
+ draw_bottom_right=True,
+ )
+
+ draw.circle(pygame.Surface((2, 2)), (0, 0, 0, 50), (1.3, 1.3), 1.2)
+
+ # def test_circle_clip(self):
+ # """ maybe useful to help work out circle clip algorithm."""
+ # MAX = max
+ # MIN = min
+ # posx=30
+ # posy=15
+ # radius=1
+ # l=29
+ # t=14
+ # r=30
+ # b=16
+ # clip_rect_x=0
+ # clip_rect_y=0
+ # clip_rect_w=30
+ # clip_rect_h=30
+
+ # l = MAX(posx - radius, clip_rect_x)
+ # t = MAX(posy - radius, clip_rect_y)
+ # r = MIN(posx + radius, clip_rect_x + clip_rect_w)
+ # b = MIN(posy + radius, clip_rect_y + clip_rect_h)
+
+ # l, t, MAX(r - l, 0), MAX(b - t, 0)
+
+ def test_circle__bounding_rect(self):
+ """Ensures draw circle returns the correct bounding rect.
+
+ Tests circles on and off the surface and a range of width/thickness
+ values.
+ """
+ circle_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ max_radius = 3
+ surface = pygame.Surface((30, 30), 0, 32)
+ surf_rect = surface.get_rect()
+ # Make a rect that is bigger than the surface to help test drawing
+ # circles off and partially off the surface. Make this rect such that
+ # when centering the test circle on one of its corners, the circle is
+ # drawn fully off the test surface, but a rect bounding the circle
+ # would still overlap with the test surface.
+ big_rect = surf_rect.inflate(max_radius * 2 - 1, max_radius * 2 - 1)
+
+ for pos in rect_corners_mids_and_center(
+ surf_rect
+ ) + rect_corners_mids_and_center(big_rect):
+ # Test using different radius and thickness values.
+ for radius in range(max_radius + 1):
+ for thickness in range(radius + 1):
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_circle(
+ surface, circle_color, pos, radius, thickness
+ )
+
+ # Calculating the expected_rect after the circle is
+ # drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(surface, surf_color, pos)
+
+ # print("pos:%s:, radius:%s:, thickness:%s:" % (pos, radius, thickness))
+ # self.assertEqual(bounding_rect, expected_rect)
+ with self.subTest(
+ surface=surface,
+ circle_color=circle_color,
+ pos=pos,
+ radius=radius,
+ thickness=thickness,
+ ):
+ self.assertEqual(bounding_rect, expected_rect)
+
+ def test_circle_negative_radius(self):
+ """Ensures negative radius circles return zero sized bounding rect."""
+ surf = pygame.Surface((200, 200))
+ color = (0, 0, 0, 50)
+ center = surf.get_height() // 2, surf.get_height() // 2
+
+ bounding_rect = self.draw_circle(surf, color, center, radius=-1, width=1)
+ self.assertEqual(bounding_rect.size, (0, 0))
+
+ def test_circle_zero_radius(self):
+ """Ensures zero radius circles does not draw a center pixel.
+
+ NOTE: This is backwards incompatible behaviour with 1.9.x.
+ """
+ surf = pygame.Surface((200, 200))
+ circle_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ center = (100, 100)
+ radius = 0
+ width = 1
+
+ bounding_rect = self.draw_circle(surf, circle_color, center, radius, width)
+ expected_rect = create_bounding_rect(surf, surf_color, center)
+ self.assertEqual(bounding_rect, expected_rect)
+ self.assertEqual(bounding_rect, pygame.Rect(100, 100, 0, 0))
+
+ def test_circle__surface_clip(self):
+ """Ensures draw circle respects a surface's clip area.
+
+ Tests drawing the circle filled and unfilled.
+ """
+ surfw = surfh = 25
+ circle_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (10, 10))
+ clip_rect.center = surface.get_rect().center
+ radius = clip_rect.w // 2 + 1
+
+ for width in (0, 1): # Filled and unfilled.
+ # Test centering the circle along the clip rect's edge.
+ for center in rect_corners_mids_and_center(clip_rect):
+ # Get the expected points by drawing the circle without the
+ # clip area set.
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_circle(surface, circle_color, center, radius, width)
+ expected_pts = get_color_points(surface, circle_color, clip_rect)
+
+ # Clear the surface and set the clip area. Redraw the circle
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_circle(surface, circle_color, center, radius, width)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure only the expected_pts
+ # are the circle_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ expected_color = circle_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(surface.get_at(pt), expected_color, pt)
+
+ surface.unlock()
+
+ def test_circle_shape(self):
+ """Ensures there are no holes in the circle, and no overdrawing.
+
+ Tests drawing a thick circle.
+ Measures the distance of the drawn pixels from the circle center.
+ """
+ surfw = surfh = 100
+ circle_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ (cx, cy) = center = (50, 50)
+ radius = 45
+ width = 25
+
+ dest_rect = self.draw_circle(surface, circle_color, center, radius, width)
+
+ for pt in test_utils.rect_area_pts(dest_rect):
+ x, y = pt
+ sqr_distance = (x - cx) ** 2 + (y - cy) ** 2
+ if (radius - width + 1) ** 2 < sqr_distance < (radius - 1) ** 2:
+ self.assertEqual(surface.get_at(pt), circle_color)
+ if (
+ sqr_distance < (radius - width - 1) ** 2
+ or sqr_distance > (radius + 1) ** 2
+ ):
+ self.assertEqual(surface.get_at(pt), surface_color)
+
+ def test_circle__diameter(self):
+ """Ensures draw circle is twice size of radius high and wide."""
+ surf = pygame.Surface((200, 200))
+ color = (0, 0, 0, 50)
+ center = surf.get_height() // 2, surf.get_height() // 2
+ width = 1
+ radius = 6
+ for radius in range(1, 65):
+ bounding_rect = self.draw_circle(surf, color, center, radius, width)
+ self.assertEqual(bounding_rect.width, radius * 2)
+ self.assertEqual(bounding_rect.height, radius * 2)
+
+ def test_x_bounds(self):
+ """ensures a circle is drawn properly when there is a negative x, or a big x."""
+
+ surf = pygame.Surface((200, 200))
+ bgcolor = (0, 0, 0, 255)
+ surf.fill(bgcolor)
+ color = (255, 0, 0, 255)
+ width = 1
+ radius = 10
+
+ where = (0, 30)
+ bounding_rect1 = self.draw_circle(surf, color, where, radius=radius)
+ self.assertEqual(
+ bounding_rect1,
+ pygame.Rect(0, where[1] - radius, where[0] + radius, radius * 2),
+ )
+ self.assertEqual(
+ surf.get_at((where[0] if where[0] > 0 else 0, where[1])), color
+ )
+ self.assertEqual(surf.get_at((where[0] + radius + 1, where[1])), bgcolor)
+ self.assertEqual(surf.get_at((where[0] + radius - 1, where[1])), color)
+
+ surf.fill(bgcolor)
+ where = (-1e30, 80)
+ bounding_rect1 = self.draw_circle(surf, color, where, radius=radius)
+ self.assertEqual(bounding_rect1, pygame.Rect(where[0], where[1], 0, 0))
+ self.assertEqual(surf.get_at((0 + radius, where[1])), bgcolor)
+
+ surf.fill(bgcolor)
+ where = (surf.get_width() + radius * 2, 80)
+ bounding_rect1 = self.draw_circle(surf, color, where, radius=radius)
+ self.assertEqual(bounding_rect1, pygame.Rect(where[0], where[1], 0, 0))
+ self.assertEqual(surf.get_at((0, where[1])), bgcolor)
+ self.assertEqual(surf.get_at((0 + radius // 2, where[1])), bgcolor)
+ self.assertEqual(surf.get_at((surf.get_width() - 1, where[1])), bgcolor)
+ self.assertEqual(surf.get_at((surf.get_width() - radius, where[1])), bgcolor)
+
+ surf.fill(bgcolor)
+ where = (-1, 80)
+ bounding_rect1 = self.draw_circle(surf, color, where, radius=radius)
+ self.assertEqual(
+ bounding_rect1,
+ pygame.Rect(0, where[1] - radius, where[0] + radius, radius * 2),
+ )
+ self.assertEqual(
+ surf.get_at((where[0] if where[0] > 0 else 0, where[1])), color
+ )
+ self.assertEqual(surf.get_at((where[0] + radius, where[1])), bgcolor)
+ self.assertEqual(surf.get_at((where[0] + radius - 1, where[1])), color)
+
+
+class DrawCircleTest(DrawCircleMixin, DrawTestCase):
+ """Test draw module function circle.
+
+ This class inherits the general tests from DrawCircleMixin. It is also
+ the class to add any draw.circle specific tests to.
+ """
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever properly supports drawing circles.
+# @unittest.skip('draw_py.draw_circle not supported yet')
+# class PythonDrawCircleTest(DrawCircleMixin, PythonDrawTestCase):
+# """Test draw_py module function draw_circle."
+#
+# This class inherits the general tests from DrawCircleMixin. It is also
+# the class to add any draw_py.draw_circle specific tests to.
+# """
+
+
+### Arc Testing ###############################################################
+
+
+class DrawArcMixin:
+ """Mixin tests for drawing arcs.
+
+ This class contains all the general arc drawing tests.
+ """
+
+ def test_arc__args(self):
+ """Ensures draw arc accepts the correct args."""
+ bounds_rect = self.draw_arc(
+ pygame.Surface((3, 3)), (0, 10, 0, 50), (1, 1, 2, 2), 0, 1, 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__args_without_width(self):
+ """Ensures draw arc accepts the args without a width."""
+ bounds_rect = self.draw_arc(
+ pygame.Surface((2, 2)), (1, 1, 1, 99), pygame.Rect((0, 0), (2, 2)), 1.1, 2.1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__args_with_negative_width(self):
+ """Ensures draw arc accepts the args with negative width."""
+ bounds_rect = self.draw_arc(
+ pygame.Surface((3, 3)), (10, 10, 50, 50), (1, 1, 2, 2), 0, 1, -1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+ self.assertEqual(bounds_rect, pygame.Rect(1, 1, 0, 0))
+
+ def test_arc__args_with_width_gt_radius(self):
+ """Ensures draw arc accepts the args with
+ width > rect.w // 2 and width > rect.h // 2.
+ """
+ rect = pygame.Rect((0, 0), (4, 4))
+ bounds_rect = self.draw_arc(
+ pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.w // 2 + 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ bounds_rect = self.draw_arc(
+ pygame.Surface((3, 3)), (10, 10, 50, 50), rect, 0, 45, rect.h // 2 + 1
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__kwargs(self):
+ """Ensures draw arc accepts the correct kwargs
+ with and without a width arg.
+ """
+ kwargs_list = [
+ {
+ "surface": pygame.Surface((4, 4)),
+ "color": pygame.Color("yellow"),
+ "rect": pygame.Rect((0, 0), (3, 2)),
+ "start_angle": 0.5,
+ "stop_angle": 3,
+ "width": 1,
+ },
+ {
+ "surface": pygame.Surface((2, 1)),
+ "color": (0, 10, 20),
+ "rect": (0, 0, 2, 2),
+ "start_angle": 1,
+ "stop_angle": 3.1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ bounds_rect = self.draw_arc(**kwargs)
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__kwargs_order_independent(self):
+ """Ensures draw arc's kwargs are not order dependent."""
+ bounds_rect = self.draw_arc(
+ stop_angle=1,
+ start_angle=2.2,
+ color=(1, 2, 3),
+ surface=pygame.Surface((3, 2)),
+ width=1,
+ rect=pygame.Rect((1, 0), (2, 3)),
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__args_missing(self):
+ """Ensures draw arc detects any missing required args."""
+ surface = pygame.Surface((1, 1))
+ color = pygame.Color("red")
+ rect = pygame.Rect((0, 0), (2, 2))
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(surface, color, rect, 0.1)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(surface, color, rect)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(surface, color)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(surface)
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc()
+
+ def test_arc__kwargs_missing(self):
+ """Ensures draw arc detects any missing required kwargs."""
+ kwargs = {
+ "surface": pygame.Surface((1, 2)),
+ "color": pygame.Color("red"),
+ "rect": pygame.Rect((1, 0), (2, 2)),
+ "start_angle": 0.1,
+ "stop_angle": 2,
+ "width": 1,
+ }
+
+ for name in ("stop_angle", "start_angle", "rect", "color", "surface"):
+ invalid_kwargs = dict(kwargs)
+ invalid_kwargs.pop(name) # Pop from a copy.
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(**invalid_kwargs)
+
+ def test_arc__arg_invalid_types(self):
+ """Ensures draw arc detects invalid arg types."""
+ surface = pygame.Surface((2, 2))
+ color = pygame.Color("blue")
+ rect = pygame.Rect((1, 1), (3, 3))
+
+ with self.assertRaises(TypeError):
+ # Invalid width.
+ bounds_rect = self.draw_arc(surface, color, rect, 0, 1, "1")
+
+ with self.assertRaises(TypeError):
+ # Invalid stop_angle.
+ bounds_rect = self.draw_arc(surface, color, rect, 0, "1", 1)
+
+ with self.assertRaises(TypeError):
+ # Invalid start_angle.
+ bounds_rect = self.draw_arc(surface, color, rect, "1", 0, 1)
+
+ with self.assertRaises(TypeError):
+ # Invalid rect.
+ bounds_rect = self.draw_arc(surface, color, (1, 2, 3, 4, 5), 0, 1, 1)
+
+ with self.assertRaises(TypeError):
+ # Invalid color.
+ bounds_rect = self.draw_arc(surface, 2.3, rect, 0, 1, 1)
+
+ with self.assertRaises(TypeError):
+ # Invalid surface.
+ bounds_rect = self.draw_arc(rect, color, rect, 0, 1, 1)
+
+ def test_arc__kwarg_invalid_types(self):
+ """Ensures draw arc detects invalid kwarg types."""
+ surface = pygame.Surface((3, 3))
+ color = pygame.Color("green")
+ rect = pygame.Rect((0, 1), (4, 2))
+ start = 3
+ stop = 4
+ kwargs_list = [
+ {
+ "surface": pygame.Surface, # Invalid surface.
+ "color": color,
+ "rect": rect,
+ "start_angle": start,
+ "stop_angle": stop,
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": 2.3, # Invalid color.
+ "rect": rect,
+ "start_angle": start,
+ "stop_angle": stop,
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": (0, 0, 0), # Invalid rect.
+ "start_angle": start,
+ "stop_angle": stop,
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "start_angle": "1", # Invalid start_angle.
+ "stop_angle": stop,
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "start_angle": start,
+ "stop_angle": "1", # Invalid stop_angle.
+ "width": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "start_angle": start,
+ "stop_angle": stop,
+ "width": 1.1,
+ },
+ ] # Invalid width.
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(**kwargs)
+
+ def test_arc__kwarg_invalid_name(self):
+ """Ensures draw arc detects invalid kwarg names."""
+ surface = pygame.Surface((2, 3))
+ color = pygame.Color("cyan")
+ rect = pygame.Rect((0, 1), (2, 2))
+ start = 0.9
+ stop = 2.3
+ kwargs_list = [
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "start_angle": start,
+ "stop_angle": stop,
+ "width": 1,
+ "invalid": 1,
+ },
+ {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "start_angle": start,
+ "stop_angle": stop,
+ "invalid": 1,
+ },
+ ]
+
+ for kwargs in kwargs_list:
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(**kwargs)
+
+ def test_arc__args_and_kwargs(self):
+ """Ensures draw arc accepts a combination of args/kwargs"""
+ surface = pygame.Surface((3, 1))
+ color = (255, 255, 0, 0)
+ rect = pygame.Rect((1, 0), (2, 3))
+ start = 0.6
+ stop = 2
+ width = 1
+ kwargs = {
+ "surface": surface,
+ "color": color,
+ "rect": rect,
+ "start_angle": start,
+ "stop_angle": stop,
+ "width": width,
+ }
+
+ for name in ("surface", "color", "rect", "start_angle", "stop_angle"):
+ kwargs.pop(name)
+
+ if "surface" == name:
+ bounds_rect = self.draw_arc(surface, **kwargs)
+ elif "color" == name:
+ bounds_rect = self.draw_arc(surface, color, **kwargs)
+ elif "rect" == name:
+ bounds_rect = self.draw_arc(surface, color, rect, **kwargs)
+ elif "start_angle" == name:
+ bounds_rect = self.draw_arc(surface, color, rect, start, **kwargs)
+ elif "stop_angle" == name:
+ bounds_rect = self.draw_arc(surface, color, rect, start, stop, **kwargs)
+ else:
+ bounds_rect = self.draw_arc(
+ surface, color, rect, start, stop, width, **kwargs
+ )
+
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__valid_width_values(self):
+ """Ensures draw arc accepts different width values."""
+ arc_color = pygame.Color("yellow")
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((6, 6))
+ rect = pygame.Rect((0, 0), (4, 4))
+ rect.center = surface.get_rect().center
+ pos = rect.centerx + 1, rect.centery + 1
+ kwargs = {
+ "surface": surface,
+ "color": arc_color,
+ "rect": rect,
+ "start_angle": 0,
+ "stop_angle": 7,
+ "width": None,
+ }
+
+ for width in (-50, -10, -3, -2, -1, 0, 1, 2, 3, 10, 50):
+ msg = f"width={width}"
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["width"] = width
+ expected_color = arc_color if width > 0 else surface_color
+
+ bounds_rect = self.draw_arc(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color, msg)
+ self.assertIsInstance(bounds_rect, pygame.Rect, msg)
+
+ def test_arc__valid_stop_angle_values(self):
+ """Ensures draw arc accepts different stop_angle values."""
+ expected_color = pygame.Color("blue")
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((6, 6))
+ rect = pygame.Rect((0, 0), (4, 4))
+ rect.center = surface.get_rect().center
+ pos = rect.centerx, rect.centery + 1
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "rect": rect,
+ "start_angle": -17,
+ "stop_angle": None,
+ "width": 1,
+ }
+
+ for stop_angle in (-10, -5.5, -1, 0, 1, 5.5, 10):
+ msg = f"stop_angle={stop_angle}"
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["stop_angle"] = stop_angle
+
+ bounds_rect = self.draw_arc(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color, msg)
+ self.assertIsInstance(bounds_rect, pygame.Rect, msg)
+
+ def test_arc__valid_start_angle_values(self):
+ """Ensures draw arc accepts different start_angle values."""
+ expected_color = pygame.Color("blue")
+ surface_color = pygame.Color("white")
+ surface = pygame.Surface((6, 6))
+ rect = pygame.Rect((0, 0), (4, 4))
+ rect.center = surface.get_rect().center
+ pos = rect.centerx + 1, rect.centery + 1
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "rect": rect,
+ "start_angle": None,
+ "stop_angle": 17,
+ "width": 1,
+ }
+
+ for start_angle in (-10.0, -5.5, -1, 0, 1, 5.5, 10.0):
+ msg = f"start_angle={start_angle}"
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["start_angle"] = start_angle
+
+ bounds_rect = self.draw_arc(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color, msg)
+ self.assertIsInstance(bounds_rect, pygame.Rect, msg)
+
+ def test_arc__valid_rect_formats(self):
+ """Ensures draw arc accepts different rect formats."""
+ expected_color = pygame.Color("red")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((6, 6))
+ rect = pygame.Rect((0, 0), (4, 4))
+ rect.center = surface.get_rect().center
+ pos = rect.centerx + 1, rect.centery + 1
+ kwargs = {
+ "surface": surface,
+ "color": expected_color,
+ "rect": None,
+ "start_angle": 0,
+ "stop_angle": 7,
+ "width": 1,
+ }
+ rects = (rect, (rect.topleft, rect.size), (rect.x, rect.y, rect.w, rect.h))
+
+ for rect in rects:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["rect"] = rect
+
+ bounds_rect = self.draw_arc(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__valid_color_formats(self):
+ """Ensures draw arc accepts different color formats."""
+ green_color = pygame.Color("green")
+ surface_color = pygame.Color("black")
+ surface = pygame.Surface((6, 6))
+ rect = pygame.Rect((0, 0), (4, 4))
+ rect.center = surface.get_rect().center
+ pos = rect.centerx + 1, rect.centery + 1
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "rect": rect,
+ "start_angle": 0,
+ "stop_angle": 7,
+ "width": 1,
+ }
+ greens = (
+ (0, 255, 0),
+ (0, 255, 0, 255),
+ surface.map_rgb(green_color),
+ green_color,
+ )
+
+ for color in greens:
+ surface.fill(surface_color) # Clear for each test.
+ kwargs["color"] = color
+
+ if isinstance(color, int):
+ expected_color = surface.unmap_rgb(color)
+ else:
+ expected_color = green_color
+
+ bounds_rect = self.draw_arc(**kwargs)
+
+ self.assertEqual(surface.get_at(pos), expected_color)
+ self.assertIsInstance(bounds_rect, pygame.Rect)
+
+ def test_arc__invalid_color_formats(self):
+ """Ensures draw arc handles invalid color formats correctly."""
+ pos = (1, 1)
+ surface = pygame.Surface((4, 3))
+ kwargs = {
+ "surface": surface,
+ "color": None,
+ "rect": pygame.Rect(pos, (2, 2)),
+ "start_angle": 5,
+ "stop_angle": 6.1,
+ "width": 1,
+ }
+
+ for expected_color in (2.3, self):
+ kwargs["color"] = expected_color
+
+ with self.assertRaises(TypeError):
+ bounds_rect = self.draw_arc(**kwargs)
+
+ def test_arc(self):
+ """Ensure draw arc works correctly."""
+ black = pygame.Color("black")
+ red = pygame.Color("red")
+
+ # create an image object of width 100, height 150, filled with black.
+ surface = pygame.Surface((100, 150))
+ surface.fill(black)
+
+ # rectangle that contains for the ellipse arc.
+ # 0 pixel from left, 0 pixel from top
+ # 80 pixels wide, 40 pixels high
+ rect = (0, 0, 80, 40)
+
+ # angle of the arc in radians
+ start_angle = 0.0
+ stop_angle = 3.14
+
+ # thickness, and it grows inward from the rectangle
+ width = 3
+
+ # draw an elliptical arc
+ pygame.draw.arc(surface, red, rect, start_angle, stop_angle, width)
+
+ # Save the drawn arc
+ pygame.image.save(surface, "arc.png")
+
+ # arc is red
+ x = 20
+ for y in range(2, 5):
+ self.assertEqual(surface.get_at((x, y)), red)
+
+ # the rest area in surface is black
+ self.assertEqual(surface.get_at((0, 0)), black)
+
+ def test_arc__bounding_rect(self):
+ """Ensures draw arc returns the correct bounding rect.
+
+ Tests arcs on and off the surface and a range of width/thickness
+ values.
+ """
+ arc_color = pygame.Color("red")
+ surf_color = pygame.Color("black")
+ min_width = min_height = 5
+ max_width = max_height = 7
+ sizes = ((min_width, min_height), (max_width, max_height))
+ surface = pygame.Surface((20, 20), 0, 32)
+ surf_rect = surface.get_rect()
+ # Make a rect that is bigger than the surface to help test drawing
+ # arcs off and partially off the surface.
+ big_rect = surf_rect.inflate(min_width * 2 + 1, min_height * 2 + 1)
+
+ # Max angle allows for a full circle to be drawn.
+ start_angle = 0
+ stop_angles = (0, 2, 3, 5, math.ceil(2 * math.pi))
+
+ for pos in rect_corners_mids_and_center(
+ surf_rect
+ ) + rect_corners_mids_and_center(big_rect):
+ # Each of the arc's rect position attributes will be set to the pos
+ # value.
+ for attr in RECT_POSITION_ATTRIBUTES:
+ # Test using different rect sizes, thickness values and stop
+ # angles.
+ for width, height in sizes:
+ arc_rect = pygame.Rect((0, 0), (width, height))
+ setattr(arc_rect, attr, pos)
+
+ for thickness in (0, 1, 2, 3, min(width, height)):
+ for stop_angle in stop_angles:
+ surface.fill(surf_color) # Clear for each test.
+
+ bounding_rect = self.draw_arc(
+ surface,
+ arc_color,
+ arc_rect,
+ start_angle,
+ stop_angle,
+ thickness,
+ )
+
+ # Calculating the expected_rect after the arc
+ # is drawn (it uses what is actually drawn).
+ expected_rect = create_bounding_rect(
+ surface, surf_color, arc_rect.topleft
+ )
+
+ self.assertEqual(
+ bounding_rect,
+ expected_rect,
+ f"thickness={thickness}",
+ )
+
+ def test_arc__surface_clip(self):
+ """Ensures draw arc respects a surface's clip area."""
+ surfw = surfh = 30
+ start = 0.1
+ end = 0 # end < start so a full circle will be drawn
+ arc_color = pygame.Color("red")
+ surface_color = pygame.Color("green")
+ surface = pygame.Surface((surfw, surfh))
+ surface.fill(surface_color)
+
+ clip_rect = pygame.Rect((0, 0), (11, 11))
+ clip_rect.center = surface.get_rect().center
+ pos_rect = clip_rect.copy() # Manages the arc's pos.
+
+ for thickness in (1, 3): # Different line widths.
+ # Test centering the arc along the clip rect's edge.
+ for center in rect_corners_mids_and_center(clip_rect):
+ # Get the expected points by drawing the arc without the
+ # clip area set.
+ pos_rect.center = center
+ surface.set_clip(None)
+ surface.fill(surface_color)
+ self.draw_arc(surface, arc_color, pos_rect, start, end, thickness)
+ expected_pts = get_color_points(surface, arc_color, clip_rect)
+
+ # Clear the surface and set the clip area. Redraw the arc
+ # and check that only the clip area is modified.
+ surface.fill(surface_color)
+ surface.set_clip(clip_rect)
+
+ self.draw_arc(surface, arc_color, pos_rect, start, end, thickness)
+
+ surface.lock() # For possible speed up.
+
+ # Check all the surface points to ensure only the expected_pts
+ # are the arc_color.
+ for pt in ((x, y) for x in range(surfw) for y in range(surfh)):
+ if pt in expected_pts:
+ expected_color = arc_color
+ else:
+ expected_color = surface_color
+
+ self.assertEqual(surface.get_at(pt), expected_color, pt)
+
+ surface.unlock()
+
+
+class DrawArcTest(DrawArcMixin, DrawTestCase):
+ """Test draw module function arc.
+
+ This class inherits the general tests from DrawArcMixin. It is also the
+ class to add any draw.arc specific tests to.
+ """
+
+
+# Commented out to avoid cluttering the test output. Add back in if draw_py
+# ever properly supports drawing arcs.
+# @unittest.skip('draw_py.draw_arc not supported yet')
+# class PythonDrawArcTest(DrawArcMixin, PythonDrawTestCase):
+# """Test draw_py module function draw_arc.
+#
+# This class inherits the general tests from DrawArcMixin. It is also the
+# class to add any draw_py.draw_arc specific tests to.
+# """
+
+
+### Draw Module Testing #######################################################
+
+
+class DrawModuleTest(unittest.TestCase):
+ """General draw module tests."""
+
+ def test_path_data_validation(self):
+ """Test validation of multi-point drawing methods.
+
+ See bug #521
+ """
+ surf = pygame.Surface((5, 5))
+ rect = pygame.Rect(0, 0, 5, 5)
+ bad_values = (
+ "text",
+ b"bytes",
+ 1 + 1j, # string, bytes, complex,
+ object(),
+ (lambda x: x),
+ ) # object, function
+ bad_points = list(bad_values) + [(1,), (1, 2, 3)] # wrong tuple length
+ bad_points.extend((1, v) for v in bad_values) # one wrong value
+ good_path = [(1, 1), (1, 3), (3, 3), (3, 1)]
+ # A) draw.lines
+ check_pts = [(x, y) for x in range(5) for y in range(5)]
+
+ for method, is_polgon in (
+ (draw.lines, 0),
+ (draw.aalines, 0),
+ (draw.polygon, 1),
+ ):
+ for val in bad_values:
+ # 1. at the beginning
+ draw.rect(surf, RED, rect, 0)
+ with self.assertRaises(TypeError):
+ if is_polgon:
+ method(surf, GREEN, [val] + good_path, 0)
+ else:
+ method(surf, GREEN, True, [val] + good_path)
+
+ # make sure, nothing was drawn :
+ self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts))
+
+ # 2. not at the beginning (was not checked)
+ draw.rect(surf, RED, rect, 0)
+ with self.assertRaises(TypeError):
+ path = good_path[:2] + [val] + good_path[2:]
+ if is_polgon:
+ method(surf, GREEN, path, 0)
+ else:
+ method(surf, GREEN, True, path)
+
+ # make sure, nothing was drawn :
+ self.assertTrue(all(surf.get_at(pt) == RED for pt in check_pts))
+
+ def test_color_validation(self):
+ surf = pygame.Surface((10, 10))
+ colors = 123456, (1, 10, 100), RED, "#ab12df", "red"
+ points = ((0, 0), (1, 1), (1, 0))
+
+ # 1. valid colors
+ for col in colors:
+ draw.line(surf, col, (0, 0), (1, 1))
+ draw.aaline(surf, col, (0, 0), (1, 1))
+ draw.aalines(surf, col, True, points)
+ draw.lines(surf, col, True, points)
+ draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150)
+ draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1)
+ draw.circle(surf, col, (7, 3), 2)
+ draw.polygon(surf, col, points, 0)
+
+ # 2. invalid colors
+ for col in (1.256, object(), None):
+ with self.assertRaises(TypeError):
+ draw.line(surf, col, (0, 0), (1, 1))
+
+ with self.assertRaises(TypeError):
+ draw.aaline(surf, col, (0, 0), (1, 1))
+
+ with self.assertRaises(TypeError):
+ draw.aalines(surf, col, True, points)
+
+ with self.assertRaises(TypeError):
+ draw.lines(surf, col, True, points)
+
+ with self.assertRaises(TypeError):
+ draw.arc(surf, col, pygame.Rect(0, 0, 3, 3), 15, 150)
+
+ with self.assertRaises(TypeError):
+ draw.ellipse(surf, col, pygame.Rect(0, 0, 3, 6), 1)
+
+ with self.assertRaises(TypeError):
+ draw.circle(surf, col, (7, 3), 2)
+
+ with self.assertRaises(TypeError):
+ draw.polygon(surf, col, points, 0)
+
+
+###############################################################################
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/event_test.py b/laplas/abstract_map/pygame/tests/event_test.py
new file mode 100644
index 00000000..eb7c437e
--- /dev/null
+++ b/laplas/abstract_map/pygame/tests/event_test.py
@@ -0,0 +1,961 @@
+import collections
+import time
+import unittest
+import os
+
+import pygame
+
+EVENT_TYPES = (
+ # pygame.NOEVENT,
+ # pygame.ACTIVEEVENT,
+ pygame.KEYDOWN,
+ pygame.KEYUP,
+ pygame.MOUSEMOTION,
+ pygame.MOUSEBUTTONDOWN,
+ pygame.MOUSEBUTTONUP,
+ pygame.JOYAXISMOTION,
+ pygame.JOYBALLMOTION,
+ pygame.JOYHATMOTION,
+ pygame.JOYBUTTONDOWN,
+ pygame.JOYBUTTONUP,
+ pygame.VIDEORESIZE,
+ pygame.VIDEOEXPOSE,
+ pygame.QUIT,
+ pygame.SYSWMEVENT,
+ pygame.USEREVENT,
+ # pygame.NUMEVENTS,
+)
+
+EVENT_TEST_PARAMS = collections.defaultdict(dict)
+EVENT_TEST_PARAMS.update(
+ {
+ pygame.KEYDOWN: {"key": pygame.K_SPACE},
+ pygame.KEYUP: {"key": pygame.K_SPACE},
+ pygame.MOUSEMOTION: {},
+ pygame.MOUSEBUTTONDOWN: {"button": 1},
+ pygame.MOUSEBUTTONUP: {"button": 1},
+ }
+)
+
+
+NAMES_AND_EVENTS = (
+ ("NoEvent", pygame.NOEVENT),
+ ("ActiveEvent", pygame.ACTIVEEVENT),
+ ("KeyDown", pygame.KEYDOWN),
+ ("KeyUp", pygame.KEYUP),
+ ("MouseMotion", pygame.MOUSEMOTION),
+ ("MouseButtonDown", pygame.MOUSEBUTTONDOWN),
+ ("MouseButtonUp", pygame.MOUSEBUTTONUP),
+ ("JoyAxisMotion", pygame.JOYAXISMOTION),
+ ("JoyBallMotion", pygame.JOYBALLMOTION),
+ ("JoyHatMotion", pygame.JOYHATMOTION),
+ ("JoyButtonDown", pygame.JOYBUTTONDOWN),
+ ("JoyButtonUp", pygame.JOYBUTTONUP),
+ ("VideoResize", pygame.VIDEORESIZE),
+ ("VideoExpose", pygame.VIDEOEXPOSE),
+ ("Quit", pygame.QUIT),
+ ("SysWMEvent", pygame.SYSWMEVENT),
+ ("MidiIn", pygame.MIDIIN),
+ ("MidiOut", pygame.MIDIOUT),
+ ("UserEvent", pygame.USEREVENT),
+ ("Unknown", 0xFFFF),
+ ("FingerMotion", pygame.FINGERMOTION),
+ ("FingerDown", pygame.FINGERDOWN),
+ ("FingerUp", pygame.FINGERUP),
+ ("MultiGesture", pygame.MULTIGESTURE),
+ ("MouseWheel", pygame.MOUSEWHEEL),
+ ("TextInput", pygame.TEXTINPUT),
+ ("TextEditing", pygame.TEXTEDITING),
+ ("ControllerAxisMotion", pygame.CONTROLLERAXISMOTION),
+ ("ControllerButtonDown", pygame.CONTROLLERBUTTONDOWN),
+ ("ControllerButtonUp", pygame.CONTROLLERBUTTONUP),
+ ("ControllerDeviceAdded", pygame.CONTROLLERDEVICEADDED),
+ ("ControllerDeviceRemoved", pygame.CONTROLLERDEVICEREMOVED),
+ ("ControllerDeviceMapped", pygame.CONTROLLERDEVICEREMAPPED),
+ ("DropFile", pygame.DROPFILE),
+ ("AudioDeviceAdded", pygame.AUDIODEVICEADDED),
+ ("AudioDeviceRemoved", pygame.AUDIODEVICEREMOVED),
+ ("DropText", pygame.DROPTEXT),
+ ("DropBegin", pygame.DROPBEGIN),
+ ("DropComplete", pygame.DROPCOMPLETE),
+)
+
+
+class EventTypeTest(unittest.TestCase):
+ def test_Event(self):
+ """Ensure an Event object can be created."""
+ e = pygame.event.Event(pygame.USEREVENT, some_attr=1, other_attr="1")
+
+ self.assertEqual(e.some_attr, 1)
+ self.assertEqual(e.other_attr, "1")
+
+ # Event now uses tp_dictoffset and tp_members:
+ # https://github.com/pygame/pygame/issues/62
+ self.assertEqual(e.type, pygame.USEREVENT)
+ self.assertIs(e.dict, e.__dict__)
+
+ e.some_attr = 12
+
+ self.assertEqual(e.some_attr, 12)
+
+ e.new_attr = 15
+
+ self.assertEqual(e.new_attr, 15)
+
+ self.assertRaises(AttributeError, setattr, e, "type", 0)
+ self.assertRaises(AttributeError, setattr, e, "dict", None)
+
+ # Ensure attributes are visible to dir(), part of the original
+ # posted request.
+ d = dir(e)
+ attrs = ("type", "dict", "__dict__", "some_attr", "other_attr", "new_attr")
+
+ for attr in attrs:
+ self.assertIn(attr, d)
+
+ # redundant type field as kwarg
+ self.assertRaises(ValueError, pygame.event.Event, 10, type=100)
+
+ def test_as_str(self):
+ # Bug reported on Pygame mailing list July 24, 2011:
+ # For Python 3.x str(event) to raises an UnicodeEncodeError when
+ # an event attribute is a string with a non-ascii character.
+ try:
+ str(pygame.event.Event(EVENT_TYPES[0], a="\xed"))
+ except UnicodeEncodeError:
+ self.fail("Event object raised exception for non-ascii character")
+ # Passed.
+
+ def test_event_bool(self):
+ self.assertFalse(pygame.event.Event(pygame.NOEVENT))
+ for event_type in [
+ pygame.MOUSEBUTTONDOWN,
+ pygame.ACTIVEEVENT,
+ pygame.WINDOWLEAVE,
+ pygame.USEREVENT_DROPFILE,
+ ]:
+ self.assertTrue(pygame.event.Event(event_type))
+
+ def test_event_equality(self):
+ """Ensure that events can be compared correctly."""
+ a = pygame.event.Event(EVENT_TYPES[0], a=1)
+ b = pygame.event.Event(EVENT_TYPES[0], a=1)
+ c = pygame.event.Event(EVENT_TYPES[1], a=1)
+ d = pygame.event.Event(EVENT_TYPES[0], a=2)
+
+ # Comparing event a
+ self.assertEqual(a, a) # Event is equal to itself
+ self.assertEqual(a, b) # Same type and attributes
+ self.assertNotEqual(a, c) # Different type but same attributes
+ self.assertNotEqual(a, d) # Same type but different attributes
+
+ # Comparing event b
+ self.assertEqual(b, a) # Same type and attributes
+ self.assertNotEqual(b, c) # Different type but same attributes
+ self.assertNotEqual(b, d) # Same type but different attributes
+
+ # Comparing event c
+ self.assertNotEqual(c, a) # Different type but same attributes
+ self.assertNotEqual(c, b) # Different type but same attributes
+ self.assertNotEqual(c, d) # Different type and different attributes
+
+ # Comparing event d
+ self.assertNotEqual(d, a) # Same type but different attributes
+ self.assertNotEqual(d, b) # Same type but different attributes
+ self.assertNotEqual(d, c) # Different type and different attributes
+
+
+race_condition_notification = """
+This test is dependent on timing. The event queue is cleared in preparation for
+tests. There is a small window where outside events from the OS may have effected
+results. Try running the test again.
+"""
+
+
+class EventModuleArgsTest(unittest.TestCase):
+ def setUp(self):
+ pygame.display.init()
+ pygame.event.clear()
+
+ def tearDown(self):
+ pygame.display.quit()
+
+ def test_get(self):
+ pygame.event.get()
+ pygame.event.get(None)
+ pygame.event.get(None, True)
+
+ pygame.event.get(pump=False)
+ pygame.event.get(pump=True)
+ pygame.event.get(eventtype=None)
+ pygame.event.get(eventtype=[pygame.KEYUP, pygame.KEYDOWN])
+ pygame.event.get(eventtype=pygame.USEREVENT, pump=False)
+
+ # event type out of range
+ self.assertRaises(ValueError, pygame.event.get, 0x00010000)
+ self.assertRaises(TypeError, pygame.event.get, 1 + 2j)
+ self.assertRaises(TypeError, pygame.event.get, "foo")
+
+ def test_clear(self):
+ pygame.event.clear()
+ pygame.event.clear(None)
+ pygame.event.clear(None, True)
+
+ pygame.event.clear(pump=False)
+ pygame.event.clear(pump=True)
+ pygame.event.clear(eventtype=None)
+ pygame.event.clear(eventtype=[pygame.KEYUP, pygame.KEYDOWN])
+ pygame.event.clear(eventtype=pygame.USEREVENT, pump=False)
+
+ # event type out of range
+ self.assertRaises(ValueError, pygame.event.clear, 0x0010FFFFF)
+ self.assertRaises(TypeError, pygame.event.get, ["a", "b", "c"])
+
+ def test_peek(self):
+ pygame.event.peek()
+ pygame.event.peek(None)
+ pygame.event.peek(None, True)
+
+ pygame.event.peek(pump=False)
+ pygame.event.peek(pump=True)
+ pygame.event.peek(eventtype=None)
+ pygame.event.peek(eventtype=[pygame.KEYUP, pygame.KEYDOWN])
+ pygame.event.peek(eventtype=pygame.USEREVENT, pump=False)
+
+ class Foo:
+ pass
+
+ # event type out of range
+ self.assertRaises(ValueError, pygame.event.peek, -1)
+ self.assertRaises(ValueError, pygame.event.peek, [-10])
+ self.assertRaises(TypeError, pygame.event.peek, Foo())
+
+
+class EventCustomTypeTest(unittest.TestCase):
+ """Those tests are special in that they need the _custom_event counter to
+ be reset before and/or after being run."""
+
+ def setUp(self):
+ pygame.quit()
+ pygame.init()
+ pygame.display.init()
+
+ def tearDown(self):
+ pygame.quit()
+
+ def test_custom_type(self):
+ self.assertEqual(pygame.event.custom_type(), pygame.USEREVENT + 1)
+ atype = pygame.event.custom_type()
+ atype2 = pygame.event.custom_type()
+
+ self.assertEqual(atype, atype2 - 1)
+
+ ev = pygame.event.Event(atype)
+ pygame.event.post(ev)
+ queue = pygame.event.get(atype)
+ self.assertEqual(len(queue), 1)
+ self.assertEqual(queue[0].type, atype)
+
+ def test_custom_type__end_boundary(self):
+ """Ensure custom_type() raises error when no more custom types.
+
+ The last allowed custom type number should be (pygame.NUMEVENTS - 1).
+ """
+ last = -1
+ start = pygame.event.custom_type() + 1
+ for _ in range(start, pygame.NUMEVENTS):
+ last = pygame.event.custom_type()
+
+ self.assertEqual(last, pygame.NUMEVENTS - 1)
+ with self.assertRaises(pygame.error):
+ pygame.event.custom_type()
+
+ def test_custom_type__reset(self):
+ """Ensure custom events get 'deregistered' by quit()."""
+ before = pygame.event.custom_type()
+ self.assertEqual(before, pygame.event.custom_type() - 1)
+ pygame.quit()
+ pygame.init()
+ pygame.display.init()
+ self.assertEqual(before, pygame.event.custom_type())
+
+
+class EventModuleTest(unittest.TestCase):
+ def _assertCountEqual(self, *args, **kwargs):
+ # Handle method name differences between Python versions.
+ # Is this still needed?
+ self.assertCountEqual(*args, **kwargs)
+
+ def _assertExpectedEvents(self, expected, got):
+ """Find events like expected events, raise on unexpected or missing,
+ ignore additional event properties if expected properties are present."""
+
+ # This does greedy matching, don't encode an NP-hard problem
+ # into your input data, *please*
+ items_left = got[:]
+ for expected_element in expected:
+ for item in items_left:
+ for key in expected_element.__dict__:
+ if item.__dict__[key] != expected_element.__dict__[key]:
+ break
+ else:
+ # found item!
+ items_left.remove(item)
+ break
+ else:
+ raise AssertionError(
+ "Expected "
+ + str(expected_element)
+ + " among remaining events "
+ + str(items_left)
+ + " out of "
+ + str(got)
+ )
+ if len(items_left) > 0:
+ raise AssertionError("Unexpected Events: " + str(items_left))
+
+ def setUp(self):
+ pygame.display.init()
+ pygame.event.clear() # flush events
+
+ def tearDown(self):
+ pygame.event.clear() # flush events
+ pygame.display.quit()
+
+ def test_event_numevents(self):
+ """Ensures NUMEVENTS does not exceed the maximum SDL number of events."""
+ # Ref: https://www.libsdl.org/tmp/SDL/include/SDL_events.h
+ MAX_SDL_EVENTS = 0xFFFF # SDL_LASTEVENT = 0xFFFF
+
+ self.assertLessEqual(pygame.NUMEVENTS, MAX_SDL_EVENTS)
+
+ def test_event_attribute(self):
+ e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1")
+ self.assertEqual(e1.attr1, "attr1")
+
+ def test_set_blocked(self):
+ """Ensure events can be blocked from the queue."""
+ event = EVENT_TYPES[0]
+ unblocked_event = EVENT_TYPES[1]
+ pygame.event.set_blocked(event)
+
+ self.assertTrue(pygame.event.get_blocked(event))
+ self.assertFalse(pygame.event.get_blocked(unblocked_event))
+
+ posted = pygame.event.post(
+ pygame.event.Event(event, **EVENT_TEST_PARAMS[event])
+ )
+ self.assertFalse(posted)
+
+ # post an unblocked event
+ posted = pygame.event.post(
+ pygame.event.Event(unblocked_event, **EVENT_TEST_PARAMS[unblocked_event])
+ )
+ self.assertTrue(posted)
+
+ ret = pygame.event.get()
+ should_be_blocked = [e for e in ret if e.type == event]
+ should_be_allowed_types = [e.type for e in ret if e.type != event]
+
+ self.assertEqual(should_be_blocked, [])
+ self.assertTrue(unblocked_event in should_be_allowed_types)
+
+ def test_set_blocked__event_sequence(self):
+ """Ensure a sequence of event types can be blocked."""
+ event_types = [
+ pygame.KEYDOWN,
+ pygame.KEYUP,
+ pygame.MOUSEMOTION,
+ pygame.MOUSEBUTTONDOWN,
+ pygame.MOUSEBUTTONUP,
+ pygame.WINDOWFOCUSLOST,
+ pygame.USEREVENT,
+ ]
+
+ pygame.event.set_blocked(event_types)
+
+ for etype in event_types:
+ self.assertTrue(pygame.event.get_blocked(etype))
+
+ def test_set_blocked_all(self):
+ """Ensure all events can be unblocked at once."""
+ pygame.event.set_blocked(None)
+
+ for e in EVENT_TYPES:
+ self.assertTrue(pygame.event.get_blocked(e))
+
+ def test_post__and_poll(self):
+ """Ensure events can be posted to the queue."""
+ e1 = pygame.event.Event(pygame.USEREVENT, attr1="attr1")
+ pygame.event.post(e1)
+ posted_event = pygame.event.poll()
+
+ self.assertEqual(e1.attr1, posted_event.attr1, race_condition_notification)
+
+ # fuzzing event types
+ for i in range(1, 13):
+ pygame.event.post(
+ pygame.event.Event(EVENT_TYPES[i], **EVENT_TEST_PARAMS[EVENT_TYPES[i]])
+ )
+
+ self.assertEqual(
+ pygame.event.poll().type, EVENT_TYPES[i], race_condition_notification
+ )
+
+ def test_post_and_get_keydown(self):
+ """Ensure keydown events can be posted to the queue."""
+ activemodkeys = pygame.key.get_mods()
+
+ events = [
+ pygame.event.Event(pygame.KEYDOWN, key=pygame.K_p),
+ pygame.event.Event(pygame.KEYDOWN, key=pygame.K_y, mod=activemodkeys),
+ pygame.event.Event(pygame.KEYDOWN, key=pygame.K_g, unicode="g"),
+ pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a, unicode=None),
+ pygame.event.Event(pygame.KEYDOWN, key=pygame.K_m, mod=None, window=None),
+ pygame.event.Event(
+ pygame.KEYDOWN, key=pygame.K_e, mod=activemodkeys, unicode="e"
+ ),
+ ]
+
+ for e in events:
+ pygame.event.post(e)
+ posted_event = pygame.event.poll()
+ self.assertEqual(e, posted_event, race_condition_notification)
+
+ def test_post_large_user_event(self):
+ pygame.event.post(
+ pygame.event.Event(
+ pygame.USEREVENT, {"a": "a" * 1024}, test=list(range(100))
+ )
+ )
+ e = pygame.event.poll()
+
+ self.assertEqual(e.type, pygame.USEREVENT)
+ self.assertEqual(e.a, "a" * 1024)
+ self.assertEqual(e.test, list(range(100)))
+
+ def test_post_blocked(self):
+ """
+ Test blocked events are not posted. Also test whether post()
+ returns a boolean correctly
+ """
+ pygame.event.set_blocked(pygame.USEREVENT)
+ self.assertFalse(pygame.event.post(pygame.event.Event(pygame.USEREVENT)))
+ self.assertFalse(pygame.event.poll())
+ pygame.event.set_allowed(pygame.USEREVENT)
+ self.assertTrue(pygame.event.post(pygame.event.Event(pygame.USEREVENT)))
+ self.assertEqual(pygame.event.poll(), pygame.event.Event(pygame.USEREVENT))
+
+ def test_get(self):
+ """Ensure get() retrieves all the events on the queue."""
+ event_cnt = 10
+ for _ in range(event_cnt):
+ pygame.event.post(pygame.event.Event(pygame.USEREVENT))
+
+ queue = pygame.event.get()
+
+ self.assertEqual(len(queue), event_cnt)
+ self.assertTrue(all(e.type == pygame.USEREVENT for e in queue))
+
+ def test_get_type(self):
+ ev = pygame.event.Event(pygame.USEREVENT)
+ pygame.event.post(ev)
+ queue = pygame.event.get(pygame.USEREVENT)
+ self.assertEqual(len(queue), 1)
+ self.assertEqual(queue[0].type, pygame.USEREVENT)
+
+ TESTEVENTS = 10
+ for _ in range(TESTEVENTS):
+ pygame.event.post(ev)
+ q = pygame.event.get([pygame.USEREVENT])
+ self.assertEqual(len(q), TESTEVENTS)
+ for event in q:
+ self.assertEqual(event, ev)
+
+ def test_get_exclude_throw(self):
+ self.assertRaises(
+ pygame.error, pygame.event.get, pygame.KEYDOWN, False, pygame.KEYUP
+ )
+
+ def test_get_exclude(self):
+ pygame.event.post(pygame.event.Event(pygame.USEREVENT))
+ pygame.event.post(pygame.event.Event(pygame.KEYDOWN))
+
+ queue = pygame.event.get(exclude=pygame.KEYDOWN)
+ self.assertEqual(len(queue), 1)
+ self.assertEqual(queue[0].type, pygame.USEREVENT)
+
+ pygame.event.post(pygame.event.Event(pygame.KEYUP))
+ pygame.event.post(pygame.event.Event(pygame.USEREVENT))
+ queue = pygame.event.get(exclude=(pygame.KEYDOWN, pygame.KEYUP))
+ self.assertEqual(len(queue), 1)
+ self.assertEqual(queue[0].type, pygame.USEREVENT)
+
+ queue = pygame.event.get()
+ self.assertEqual(len(queue), 2)
+
+ def test_get__empty_queue(self):
+ """Ensure get() works correctly on an empty queue."""
+ expected_events = []
+ pygame.event.clear()
+
+ # Ensure all events can be checked.
+ retrieved_events = pygame.event.get()
+
+ self.assertListEqual(retrieved_events, expected_events)
+
+ # Ensure events can be checked individually.
+ for event_type in EVENT_TYPES:
+ retrieved_events = pygame.event.get(event_type)
+
+ self.assertListEqual(retrieved_events, expected_events)
+
+ # Ensure events can be checked as a sequence.
+ retrieved_events = pygame.event.get(EVENT_TYPES)
+
+ self.assertListEqual(retrieved_events, expected_events)
+
+ def test_get__event_sequence(self):
+ """Ensure get() can handle a sequence of event types."""
+ event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION]
+ other_event_type = pygame.MOUSEBUTTONUP
+
+ # Test when no events in the queue.
+ expected_events = []
+ pygame.event.clear()
+ retrieved_events = pygame.event.get(event_types)
+
+ # don't use self._assertCountEqual here. This checks for
+ # expected properties in events, and ignores unexpected ones, for
+ # forward compatibility with SDL2.
+ self._assertExpectedEvents(expected=expected_events, got=retrieved_events)
+
+ # Test when an event type not in the list is in the queue.
+ expected_events = []
+ pygame.event.clear()
+ pygame.event.post(
+ pygame.event.Event(other_event_type, **EVENT_TEST_PARAMS[other_event_type])
+ )
+
+ retrieved_events = pygame.event.get(event_types)
+
+ self._assertExpectedEvents(expected=expected_events, got=retrieved_events)
+
+ # Test when 1 event type in the list is in the queue.
+ expected_events = [
+ pygame.event.Event(event_types[0], **EVENT_TEST_PARAMS[event_types[0]])
+ ]
+ pygame.event.clear()
+ pygame.event.post(expected_events[0])
+
+ retrieved_events = pygame.event.get(event_types)
+
+ self._assertExpectedEvents(expected=expected_events, got=retrieved_events)
+
+ # Test all events in the list are in the queue.
+ pygame.event.clear()
+ expected_events = []
+
+ for etype in event_types:
+ expected_events.append(
+ pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype])
+ )
+ pygame.event.post(expected_events[-1])
+
+ retrieved_events = pygame.event.get(event_types)
+
+ self._assertExpectedEvents(expected=expected_events, got=retrieved_events)
+
+ def test_get_clears_queue(self):
+ """Ensure get() clears the event queue after a call"""
+ pygame.event.get() # should clear the queue completely by getting all events
+ self.assertEqual(pygame.event.get(), [])
+
+ def test_clear(self):
+ """Ensure clear() removes all the events on the queue."""
+ for e in EVENT_TYPES:
+ pygame.event.post(pygame.event.Event(e, **EVENT_TEST_PARAMS[e]))
+ poll_event = pygame.event.poll()
+
+ self.assertNotEqual(poll_event.type, pygame.NOEVENT)
+
+ pygame.event.clear()
+ poll_event = pygame.event.poll()
+
+ self.assertEqual(poll_event.type, pygame.NOEVENT, race_condition_notification)
+
+ def test_clear__empty_queue(self):
+ """Ensure clear() works correctly on an empty queue."""
+ expected_events = []
+ pygame.event.clear()
+
+ # Test calling clear() on an already empty queue.
+ pygame.event.clear()
+
+ retrieved_events = pygame.event.get()
+
+ self.assertListEqual(retrieved_events, expected_events)
+
+ def test_clear__event_sequence(self):
+ """Ensure a sequence of event types can be cleared from the queue."""
+ cleared_event_types = EVENT_TYPES[:5]
+ expected_event_types = EVENT_TYPES[5:10]
+ expected_events = []
+
+ # Add the events to the queue.
+ for etype in cleared_event_types:
+ pygame.event.post(pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype]))
+
+ for etype in expected_events:
+ expected_events.append(
+ pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype])
+ )
+ pygame.event.post(expected_events[-1])
+
+ # Clear the cleared_events from the queue.
+ pygame.event.clear(cleared_event_types)
+
+ # Check the rest of the events in the queue.
+ remaining_events = pygame.event.get()
+
+ self._assertCountEqual(remaining_events, expected_events)
+
+ def test_event_name(self):
+ """Ensure event_name() returns the correct event name."""
+ for expected_name, event in NAMES_AND_EVENTS:
+ self.assertEqual(
+ pygame.event.event_name(event), expected_name, f"0x{event:X}"
+ )
+
+ def test_event_name__userevent_range(self):
+ """Ensures event_name() returns the correct name for user events.
+
+ Tests the full range of user events.
+ """
+ expected_name = "UserEvent"
+
+ for event in range(pygame.USEREVENT, pygame.NUMEVENTS):
+ self.assertEqual(
+ pygame.event.event_name(event), expected_name, f"0x{event:X}"
+ )
+
+ def test_event_name__userevent_boundary(self):
+ """Ensures event_name() does not return 'UserEvent' for events
+ just outside the user event range.
+ """
+ unexpected_name = "UserEvent"
+
+ for event in (pygame.USEREVENT - 1, pygame.NUMEVENTS):
+ self.assertNotEqual(
+ pygame.event.event_name(event), unexpected_name, f"0x{event:X}"
+ )
+
+ def test_event_name__kwargs(self):
+ """Ensure event_name() returns the correct event name when kwargs used."""
+ for expected_name, event in NAMES_AND_EVENTS:
+ self.assertEqual(
+ pygame.event.event_name(type=event), expected_name, f"0x{event:X}"
+ )
+
+ def test_peek(self):
+ """Ensure queued events can be peeked at."""
+ event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION]
+
+ for event_type in event_types:
+ pygame.event.post(
+ pygame.event.Event(event_type, **EVENT_TEST_PARAMS[event_type])
+ )
+
+ # Ensure events can be checked individually.
+ for event_type in event_types:
+ self.assertTrue(pygame.event.peek(event_type))
+
+ # Ensure events can be checked as a sequence.
+ self.assertTrue(pygame.event.peek(event_types))
+
+ def test_peek__event_sequence(self):
+ """Ensure peek() can handle a sequence of event types."""
+ event_types = [pygame.KEYDOWN, pygame.KEYUP, pygame.MOUSEMOTION]
+ other_event_type = pygame.MOUSEBUTTONUP
+
+ # Test when no events in the queue.
+ pygame.event.clear()
+ peeked = pygame.event.peek(event_types)
+
+ self.assertFalse(peeked)
+
+ # Test when an event type not in the list is in the queue.
+ pygame.event.clear()
+ pygame.event.post(
+ pygame.event.Event(other_event_type, **EVENT_TEST_PARAMS[other_event_type])
+ )
+
+ peeked = pygame.event.peek(event_types)
+
+ self.assertFalse(peeked)
+
+ # Test when 1 event type in the list is in the queue.
+ pygame.event.clear()
+ pygame.event.post(
+ pygame.event.Event(event_types[0], **EVENT_TEST_PARAMS[event_types[0]])
+ )
+
+ peeked = pygame.event.peek(event_types)
+
+ self.assertTrue(peeked)
+
+ # Test all events in the list are in the queue.
+ pygame.event.clear()
+ for etype in event_types:
+ pygame.event.post(pygame.event.Event(etype, **EVENT_TEST_PARAMS[etype]))
+
+ peeked = pygame.event.peek(event_types)
+
+ self.assertTrue(peeked)
+
+ def test_peek__empty_queue(self):
+ """Ensure peek() works correctly on an empty queue."""
+ pygame.event.clear()
+
+ # Ensure all events can be checked.
+ peeked = pygame.event.peek()
+
+ self.assertFalse(peeked)
+
+ # Ensure events can be checked individually.
+ for event_type in EVENT_TYPES:
+ peeked = pygame.event.peek(event_type)
+ self.assertFalse(peeked)
+
+ # Ensure events can be checked as a sequence.
+ peeked = pygame.event.peek(EVENT_TYPES)
+
+ self.assertFalse(peeked)
+
+ def test_set_allowed(self):
+ """Ensure a blocked event type can be unblocked/allowed."""
+ event = EVENT_TYPES[0]
+ pygame.event.set_blocked(event)
+
+ self.assertTrue(pygame.event.get_blocked(event))
+
+ pygame.event.set_allowed(event)
+
+ self.assertFalse(pygame.event.get_blocked(event))
+
+ def test_set_allowed__event_sequence(self):
+ """Ensure a sequence of blocked event types can be unblocked/allowed."""
+ event_types = [
+ pygame.KEYDOWN,
+ pygame.KEYUP,
+ pygame.MOUSEMOTION,
+ pygame.MOUSEBUTTONDOWN,
+ pygame.MOUSEBUTTONUP,
+ ]
+ pygame.event.set_blocked(event_types)
+
+ pygame.event.set_allowed(event_types)
+
+ for etype in event_types:
+ self.assertFalse(pygame.event.get_blocked(etype))
+
+ def test_set_allowed_all(self):
+ """Ensure all events can be unblocked/allowed at once."""
+ pygame.event.set_blocked(None)
+
+ for e in EVENT_TYPES:
+ self.assertTrue(pygame.event.get_blocked(e))
+
+ pygame.event.set_allowed(None)
+
+ for e in EVENT_TYPES:
+ self.assertFalse(pygame.event.get_blocked(e))
+
+ def test_pump(self):
+ """Ensure pump() functions properly."""
+ pygame.event.pump()
+
+ # @unittest.skipIf(
+ # os.environ.get("SDL_VIDEODRIVER") == "dummy",
+ # 'requires the SDL_VIDEODRIVER to be a non "dummy" value',
+ # )
+ # Fails on SDL 2.0.18
+ @unittest.skip("flaky test, and broken on 2.0.18 windows")
+ def test_set_grab__and_get_symmetric(self):
+ """Ensure event grabbing can be enabled and disabled.
+
+ WARNING: Moving the mouse off the display during this test can cause it
+ to fail.
+ """
+ surf = pygame.display.set_mode((10, 10))
+ pygame.event.set_grab(True)
+
+ self.assertTrue(pygame.event.get_grab())
+
+ pygame.event.set_grab(False)
+
+ self.assertFalse(pygame.event.get_grab())
+
+ def test_get_blocked(self):
+ """Ensure an event's blocked state can be retrieved."""
+ # Test each event is not blocked.
+ pygame.event.set_allowed(None)
+
+ for etype in EVENT_TYPES:
+ blocked = pygame.event.get_blocked(etype)
+
+ self.assertFalse(blocked)
+
+ # Test each event type is blocked.
+ pygame.event.set_blocked(None)
+
+ for etype in EVENT_TYPES:
+ blocked = pygame.event.get_blocked(etype)
+
+ self.assertTrue(blocked)
+
+ def test_get_blocked__event_sequence(self):
+ """Ensure get_blocked() can handle a sequence of event types."""
+ event_types = [
+ pygame.KEYDOWN,
+ pygame.KEYUP,
+ pygame.MOUSEMOTION,
+ pygame.MOUSEBUTTONDOWN,
+ pygame.MOUSEBUTTONUP,
+ pygame.WINDOWMINIMIZED,
+ pygame.USEREVENT,
+ ]
+
+ # Test no event types in the list are blocked.
+ blocked = pygame.event.get_blocked(event_types)
+
+ self.assertFalse(blocked)
+
+ # Test when 1 event type in the list is blocked.
+ pygame.event.set_blocked(event_types[2])
+
+ blocked = pygame.event.get_blocked(event_types)
+
+ self.assertTrue(blocked)
+
+ # Test all event types in the list are blocked.
+ pygame.event.set_blocked(event_types)
+
+ blocked = pygame.event.get_blocked(event_types)
+
+ self.assertTrue(blocked)
+
+ # @unittest.skipIf(
+ # os.environ.get("SDL_VIDEODRIVER") == "dummy",
+ # 'requires the SDL_VIDEODRIVER to be a non "dummy" value',
+ # )
+ # Fails on SDL 2.0.18
+ @unittest.skip("flaky test, and broken on 2.0.18 windows")
+ def test_get_grab(self):
+ """Ensure get_grab() works as expected"""
+ surf = pygame.display.set_mode((10, 10))
+ # Test 5 times
+ for i in range(5):
+ pygame.event.set_grab(i % 2)
+ self.assertEqual(pygame.event.get_grab(), i % 2)
+
+ @unittest.skipIf(
+ os.environ.get("SDL_VIDEODRIVER") == "dummy",
+ "requires the SDL_VIDEODRIVER to be a non dummy value",
+ )
+ @unittest.skipIf(pygame.get_sdl_version() < (2, 0, 16), "Needs at least SDL 2.0.16")
+ def test_set_keyboard_grab_and_get_keyboard_grab(self):
+ """Ensure set_keyboard_grab() and get_keyboard_grab() work as expected"""
+
+ surf = pygame.display.set_mode((10, 10))
+
+ pygame.event.set_keyboard_grab(True)
+ self.assertTrue(pygame.event.get_keyboard_grab())
+
+ pygame.event.set_keyboard_grab(False)
+ self.assertFalse(pygame.event.get_keyboard_grab())
+
+ def test_poll(self):
+ """Ensure poll() works as expected"""
+ pygame.event.clear()
+ ev = pygame.event.poll()
+ # poll() on empty queue should return NOEVENT
+ self.assertEqual(ev.type, pygame.NOEVENT)
+
+ # test poll returns stuff in same order
+ e1 = pygame.event.Event(pygame.USEREVENT)
+ e2 = pygame.event.Event(pygame.KEYDOWN, key=pygame.K_a)
+ e3 = pygame.event.Event(pygame.KEYUP, key=pygame.K_a)
+ pygame.event.post(e1)
+ pygame.event.post(e2)
+ pygame.event.post(e3)
+
+ self.assertEqual(pygame.event.poll().type, e1.type)
+ self.assertEqual(pygame.event.poll().type, e2.type)
+ self.assertEqual(pygame.event.poll().type, e3.type)
+ self.assertEqual(pygame.event.poll().type, pygame.NOEVENT)
+
+
+class EventModuleTestsWithTiming(unittest.TestCase):
+ __tags__ = ["timing"]
+
+ def setUp(self):
+ pygame.display.init()
+ pygame.event.clear() # flush events
+
+ def tearDown(self):
+ pygame.event.clear() # flush events
+ pygame.display.quit()
+
+ def test_event_wait(self):
+ """Ensure wait() waits for an event on the queue."""
+ # Test case without timeout.
+ event = pygame.event.Event(EVENT_TYPES[0], **EVENT_TEST_PARAMS[EVENT_TYPES[0]])
+ pygame.event.post(event)
+ wait_event = pygame.event.wait()
+
+ self.assertEqual(wait_event.type, event.type)
+
+ # Test case with timeout and no event in the queue.
+ wait_event = pygame.event.wait(100)
+ self.assertEqual(wait_event.type, pygame.NOEVENT)
+
+ # Test case with timeout and an event in the queue.
+ event = pygame.event.Event(EVENT_TYPES[0], **EVENT_TEST_PARAMS[EVENT_TYPES[0]])
+ pygame.event.post(event)
+ wait_event = pygame.event.wait(100)
+
+ self.assertEqual(wait_event.type, event.type)
+
+ # test wait with timeout waits for the correct duration
+ pygame.time.set_timer(pygame.USEREVENT, 50, 3)
+
+ for wait_time, expected_type, expected_time in (
+ (60, pygame.USEREVENT, 50),
+ (65, pygame.USEREVENT, 50),
+ (20, pygame.NOEVENT, 20),
+ (45, pygame.USEREVENT, 30),
+ (70, pygame.NOEVENT, 70),
+ ):
+ start_time = time.perf_counter()
+ self.assertEqual(pygame.event.wait(wait_time).type, expected_type)
+ self.assertAlmostEqual(
+ time.perf_counter() - start_time, expected_time / 1000, delta=0.01
+ )
+
+ # test wait without timeout waits for the full duration
+ pygame.time.set_timer(pygame.USEREVENT, 100, 1)
+
+ start_time = time.perf_counter()
+ self.assertEqual(pygame.event.wait().type, pygame.USEREVENT)
+ self.assertAlmostEqual(time.perf_counter() - start_time, 0.1, delta=0.01)
+
+ # test wait returns no event if event is arriving later
+ pygame.time.set_timer(pygame.USEREVENT, 50, 1)
+ self.assertEqual(pygame.event.wait(40).type, pygame.NOEVENT)
+
+
+################################################################################
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/laplas/abstract_map/pygame/tests/fixtures/fonts/A_PyGameMono-8.png b/laplas/abstract_map/pygame/tests/fixtures/fonts/A_PyGameMono-8.png
new file mode 100644
index 0000000000000000000000000000000000000000..b15961f08bce5b630952beb840bda9dab3db338f
GIT binary patch
literal 92
zcmeAS@N?(olHy`uVBq!ia0vp^Y(UJ;$P6R}bUzs;=I*>bF()emY#j<=}BhGI+^SQ!k|fbfqYRB
z6-020ApY<*3Ti~~hl=2iMg@KK^Tl2HUR00}NHR6w|2gMYcXdzC62LdVWYY8Wx#!;V
zoM&IoVVp7M#UC1LnVXs2_0wTQ!|W(zO-kxv*)cpyLS&$TKh1|>D2?12mUekdv&J%SPp+5xZ~dSOOxJ(75x4^
zjF~Rn@yJ6hEhX27m{R*c`2F+eE}g&kg9Cw=n9}_^W6r?&^#?Bj?#BBM;Q!|H_dI^?
z#XtQ+{K(swVtnMn+4Va;AAahyObP!9fCCo*Fnz^%2j+L<@3{;2KJ?g|-uGypDG@Vc
z9`il--?6U#&PN8BV)_jJZohZ^u}g+OcYOeVe;(so?pwe2><51E6PK7W`TdL;?z?pV
zgAX0}!088=GKXJR4qUo?_R^<*`MtnL%@4pAn8HT!E#FWzrZN+2tra!SnWA##JOE5R
z%v6=F;Rm({%QYHknCdYYLzPKS+G9w2()=6sUHpgZ&+*Hu_WJ9pmb{9OyY^p2C-_P}pD*Q0-ojMa
zJLR)j6O~C{Fc=CH3fYX&)ITs#RI93S3#94`_V@znDqsIESJo9>D6}nwd*l8Wygw0D
zI$B#slfj}ZSn>apJ$^_13s&WGSO1?}jQbVN6~B_|<$oQluTRazb%>$0Pua`s$Nc_l
z*W^OfUoaLJQI$!HvX>d#fd7CFH)_dq8&GXlhZYFRDK{t7*PZBF?i
zwehqVhq*cX$IZE~n77ds~M6herRJ&iAx)6QlH;|
z#vj~(YVX>Z6HtAneNy5{Fr
z>-e%!FytAtWXM=;Sx)D@@?DwlXl?k=p_P^0yFDJp4xKx6?v69-r%#?(Ik<9Q|K8op
zyO)<1Jqw=csma0awn{0VjDiYVxv^OqRJp-u4ETegY=@GCgfIp?{$Q}T
zw`X9W+ADrRf`cT-xYF&tLy3604?dt2U8iS1~`sqRbZ}o>`v0hhq
z%#(MxTLS!j{FtUHJYC^Rd7(2Nx7gC@?t9l0mHX>2
ze$mQ5^`YO^xT1wLM>?Brv1zJy_0EnQL=MRv9?vQ-)Qa;yU)Dak)MCr#>+W36mOD6~
z>h>>{q-?V1a{NE@SqJxXh1Wmo$3GAC;`94^;{zFgc_0A!4ZWn?qkM+t**Kf1jXJoA
zFUB}uz)l+#2w1bGNEyz?mknG|_!<*ZwpbV%=%YtQo;YT87X%g*h^;whB2#c+igG}WLzi6xCMldf7fLr%{uGox_=G^?4`
znaMP3TEWw8MXVzc^LcDmlaclDK8rP2nd}LMfUB?2P4Dilmc
zLPMdyZ(x8wHFYrU4`?Ap2^pP{U~IA6x7-_v_3!D+W3gcq{X+8v^Q)8Re)QmxA5~r|
zMqQyw;l5+-V{QIqqPq6v+`?N<_Qey))!9=I7Arw#w0P*JE?@fDQ`fG^H3~r!mFkA)
zNkr&y63r5xU_YV5X9y=7@Ep%JjVG}y$H&=EH^E8qNO+i&86b)1vml}btFU)`%mMO|
zVkBac)MErzw~|k6@dXFuOpd9VqN$geVlXI|OkC60nj8$p&QD8Hi`8yC&(Wi`Pzsho
zA(yPADxsjuX*aS251Zxg5Fu`-KrjIICbU6^M0|Yt)4z87qfZ2nWHv|+Gqn3D-HmUKTk);7u#i(f@b&%EAKK8oA=mh3;aCVtl&8pd|
zUSX6~96GOnKQ4u{*mbZEZ={SxeelIJ}hV4(zSmKG#ZJF4rRvj{Fkv+Q
zf;6mH4yMYq`tSPFMfFv7Kl?POm9xLcbHdpCtxaN`zHY_{d-L`gv8GEJ8ClJ5&hSZL
zWZ})y$daqk$jE9Q*TNQem~UDbS#o`$CpOk6OkaYUv@o0y0jKo{u9I-Fjt$(A0WN2W
zym@mLGQ8yI410GI{>ku?qto}xxo4D>rn&R{iKfwHV9C)fi(m=X20ha)*mhrSg>!?c
zkXIq%jLH%-8dQUkc$OPf4yB_RICPJ}U|NGe19oMKBGe9B$J%Du3jQ2qy}nY`@6ELe
zPbQ!ig;ph@Ce)45l!a#P6&i;GS+Ur&}p}yhw_J(TqR#bkUY>V7A+*&06rQL?O;QKbpjRbte@>-AN_bdsHnPT7zV2B
zoY}9qxZP~FUt$)MVzMY#Y|!UcZnBt7cNrkajB5^VG>$KW0Tp!}8hS!qiPc7$AI9@f
zzJaHY)_|Ih4miuZ26n+ltc{I!^mp|2bm1$DA@5YEH|Vy;ca%6AK4im|(JjGSh#6a<
z)?a~`ad%V_C%Ri#63xQp;wS%12p26#!X=<7pQ-E(gi5Z(!+8=e_CmqV13QV85BdFD
z#R^ET6QuVEm^f4HObuolEJjY$X3|s>q_fdrv>IT+o4FAt%ta6zC=LpD9s58wkHrGw
zGZ<{^%x0Ul!R8qo8R+lr>Fj7P5pDK7cV>9R(GYzc#{2S6P%>3geTbZO>mOC5*}wC
z*5OV<{suhDTbjmi&L8Fpyk8OD^XFo-cCpV&YiRzuJ#!cw*2`kc?6hTe7k60gj`N#)
z=iG7cVzq@Gr_#6o4b645R-B&ZY;j}I=yOTQ@P;N1)Wb8pw6W>^knhI}v-3F(3
z!mu(kGcz|cw`+EKVr;a(Z=J-5XFcR`SyPquX1|eGBLEM9Tufb>7;f=~_>D9-E>pz|dd@
znEE1oM=ZNe%1*9Dt^0}s8Bk$2UoWi<0hJFapV11?
zwO9q)&+lxkg5ND91;LG4^gOJ9YyU6S@GfC_NNDme3Hq;q{tpq}lF;}+k;Q<2_yfXr
zk#7!Y`kOt;P8Z4O{El9N|fhz?=C&dQKD1kqskfi@d%$8`&@t
zn(+a8?VA|;m`WCmgr@&PjJ-C)mzw6yKTc!+5o6z}lKmpbw)_DA*S?IgQ`oBnE7o!#
zdC(5dRyJn4lu%llZa{+s%&F4sqB|Mwl${
zC{!!rSK1QehTX$`HWi^QmBE@(E?SL>rWw}kHu#t0%djyxmp=XkTPc^zyUVjP)1xC(
zA@5|+6ZD*LfMF-0tV||^gc}?xV#YlXmdWjGV?DJ_U>)&%e#HQGHLjU-
zTa0t{w6w+WL>tT|^3*(LGMgGmxkUhtSa&g#wcC>Mp}zSx^L4Hln7F*R!Ng{`SUsnv
z)BVByZP(jEiY}(4Lvlq_pDt75T?
zFPjWPi>w6`BQH|^zC-)Jn{?UqWz-5t*FihVKor>?YX6W53$HY*6FgQi#69+Js6tB`7J%OM+q~oQy;XPw0WNJeUi(
z*nnp_@^2)y_Ji>}V~_*q-Ix^OF-JL-?{z
zu@woAg90ytpVc?A6{<(Z++gC%NU%vbq5Yn&9_6Eub}pmCRNrsUX1ra|P`5|YqNh6)
z>GJXW3EUltbdTq1ZIxOv5ij_C2Ag5DFVv6)zf}LK*t)`2I^BqKBhVe;Z)s3x{nh%j
zcRx_hloIJw)SE^^;=c3z-5VQ9x=(Ig3wu^<5Jidq3Rby`rC58dm7-M)iACWcfpynG
zva@`}fFnHjTuaInUg5rU?7&bu$5(A|%u_
z6W*EO&u-1U$yJJ=(AJSH?1y%mQPDiKD5!Ot;3O?)mHMk
zfWN(x>&^EjV*YF(8!EXilyX9jz$D`TI!D05DE0(zPA*@n9mpvDmRvHCODE&2)i>RU
ze11doc+Iv_EMCYbq8)CRkGI~6p#DK1{tp=;zKcjES5%+NKa`TkW;&^d{^hL9c-Rfb=jW$u6#2h
z@V%5t9UkIr<@chQMa?Vw%k@wj0^CckVRrDqDpX}un;r89u_MX1}j9Kah*ur
zxDtzT78{LK2l_kOE5&>+5fAyCHfX>o58I^%4ESkXn@L@)EL*%j=&`v%giwJ5J}#Gd
z`Vf{lxi7WkQ92LBZsZ#EUpQLcH(m5sPxK!>srZ!dyM+@Zr233%f*2^WD#APaYkLxL
z#b5$i7}EGdU81EUKY!5z4@W~1Sku7nCX?91S(zEe#xAgxp}}%#a$<09Xs)}nR4rHA
zv!#L+KJeL)aCb@h7xAwit<@<+*P}X`5UMLZtR)&d%~HXprUb$)ozd2t&yPWhDl~IIZdje
zq`ZDZ)j-q%-S;bkwow^U+do;uG$3)S0l5pa!WA>}v^KciTZ!Ewd~8AO%i?G2I*ig;
z>&o)t?DY7kr@;W^*1S;$@Ff)pnh^f0f{K7+%6q^U&t|uMOWNc@8ft`OOeQ#y7K7Bco&cTa)8^2FLdlfFgkkfzYRPg#M{OsDu+j
z2@ZrV5kiOHO#yK_)THGQPLpsTR9LWbbbOD5I=76@vfmKsw*md15`p*wwS9#=vL=ca
zgCHD6`iUfiX3(fogf>7{hnPqvu1WR3=@W1^Ix^JL-PKx3C4zp3jq)Uo8WFI+UWowV
zu+1?NSv#M|jR-9e3P6TncZ|A_9N^C_J<<80U3d0RKeCeV^2TGOM553Vv}Ek96WL4W
zKH?b+_aE%@COj>btgp9=y!+>sm&%#{aE%@4A6*-;nVs#ibh+4)%$v+of93THM2(ld*`Jcc@7h9~&JCRO-{fnwgMB1+4w~8q5Bv&3d
z5yp?8Nr@d~Ejeo~Wzxw+IA}+n2{oTxyo(Sqi*;4gp)_hQA@Zw5QhkCBVIP>XAh%v{
z`8Y~vq+!fYAAEK{R}xD7l2+V5vT(|V?dG(0(or)djSPOjZ(wEU-1`n6{1NBW
zvv>P;zh(DRKWuO$oky>%mLk#U{_$h?6*{~QM>2Tu`9~jl{t!Mt1{HW&2)Uu!K-i0n
zq!GbG1?xhTi%KVCE*3U~qUsq(kV!Xd_2s-%{%la>VVlgJ>*6tD?>6$U2*)5iaamY0
z&eCLmi>sq8dAg_Xc)Zf#PWMff93t=9cGq2PmAmdzKGShHg!JFu^DrBLW_hm1++3jy`Rb>(xKfgP@YbLo$22scs0s{TzGu@b{X{Es|bu+9U-
z3^A|{upl3X8DIB
z!s80hy@8r^8@&3
z@`HT>0TNDrwS*^0;s9Y1P7+AM6YRMrIQiQW9;g3rjHen=2@mOM!T`K$AaVoFx4f4e
z8#zA0ezs{I@>3-|qf0INo_~YQHpAv?v+YGiF^v~8;7q7$WYV8DHw796qq21iw1;3%
zwZeufi#wr=~<{K1(@~uklH8smI@NX4SAM7zdeWMN2BE4aQC+BY&
zO(?!Jd9}HCyeDj-U3T7L-#%!eibuulH|lO~yv^-)a&!G&vFkdv|4r;BlDAuSbB?eC
z>`8btwwuQ{WBcEPQ!B>{Y;Vj@;UawCeJMb=qsBPSy
zgCbJ=42A^-nTsqF_N@C4%Dfu(OckY>S(Wnia#xZ12qA_%Ky@SnqP*AN<@;+y>`V^h
zYg8M*m_r7#JL!MX_rsC4a(W~Y&Lc8s>h8PxYk22_`6y*2DF2@9s~1E3`=?h#oPv7{h@Le~vef7RdR{$;Gq=rI1dE#V5!W?FZLQW=B&
z`~8mvRa#v#l)t(d@~YmMO5*|T!
z2*t7xGNA+~a&_InIsCm|&a^65SO>mCMP@?bi#=gQ+ud$K<+lk=Ak)^&7NpD?#hO%1
zh?WY*rYFY7Py(CBT#$
zrN|F0%~41n)c6{3vj*H`;Sr)B7zp9jfm~S7Vpl8NV*DO?3&RzXseBD(&_tEO^wZs8
z#(KItD`gNROGZ3r15^Pm$&y$q+h8eJa-hG>yy1WD*xg%y8-F1iz;YEOV2(!WzaRhm
zsog0-boC$j@>WmgkH$e(V#$KsJ}SwLeN~W;gcG?D+^EH=>P;U_#3o0F*wb<}6c`Y<
z0q5-VO|uXY%JC8QOcR_Gn1pB8k2S$bq{+9opk$1L#nHPYL>fc{HqQ{C-9kG8!mye!
zKapE|5|Ow@@c_P9IlWOk02$}Yob_jt=1U~52dyK-e-!FeHjBK
zA*=7Ij=yt^-}R2`D&nr54|eVyPWNw=dA?87z=h=Bp(Dhn3-o?mNALflh3as!90)FI
z<2Im#MnFX|!0)o{#Q-zPAF02fmnRgc3;{BHKfg!L&VOIbPVwq1>aSy-sDKyvq=3uJ
z6v1VJqrhi2#{ZQVf2TfvS-_=-MdQh5GU2UX5pcOGg3DDE*f;Qdg|%G!7JpswfqSMY
z8Se%EjE?koXhx-zYr=2hNOHn|#U_#&J-3XyGb39!m>^~JDo6P<1ywj3oF1C)?QUz$
zv>>tKWK~`zpW$!@?$Wp>ipj8ZNF~XZ0{BqiD7XrU3{dEd911-;U^G&wpx;Ss_Q9#q
z(!{Ruk}n?*bhx64aw?sPM181;C<}RAZ9FZ;VTZV6
zBz>0pwpm_bXRn(DJEp$2b)3c!?%)148uy1=$EkJD@lNA9^l?V@wYJSQZ9-oi*nn!C
z8*>6GR{9g{YjUMum(Ge@Y2)dFigDXlsFf>%ZOsjvtb)3d{3O2aF>qi@J
zqm^!APJR_Cy04!PwEkZkD}8xPt_i!M_;l#@{TSaE{R;K=>AN)2fNCh=Z$OpbZ9vs)
za}qkUWo8X&;*HU2Y-99QQOqo<$i4xs!(>wNzOO021^%pnLr<`KYj+Wc7P(nDdZeop
zemg}rI4l736_^Po&1^bvA!Uim1maaYBA^V_ZGu&;R>QgjF^^dTIJLcvvqJ|K7f=j1
zjX>y$p%ZP@_Nvac6<%@PF4vlz3NdO?FseaskD>fny4X7)l_NM>&!AEa^=8Z*a-NEf
zj-{hr0kgBWFJI)dwYC1fJ7!nc>c7W7qc~Ftio$E<`Qet-MAT>NoWB*9_xwX&)j!x$
z`b01;n0sRQk)wy6oVn{EU-xRy(X&QJ(lvXmHQQ&-&Y~3;jdlO2L{t}|3mt5!wvdQ~
zt#FbvFe{X$R>Ycjr>Un(&kCR)j(Uv{cVKgrNNjk|h`hj_2>M+PvysJk%miGb#i$WF
zLVN^rj&jK)3K2gkdJtKrI0{^6*cQm2yB2AKsM|~cvnYHp(Jv&aKTJ(dF
z@TN5&G9dHgw7UrG$_A-uej*f(my)SsCTuB$l3iI}aXgz^iu-KcD}%KQW8?QLK99?V
zm{tjHWWnh0q{fa8?>kk@^_#N0Iz}EldgO^I@Rs;=A&bAL%VKD-4LHddf~hbhaA^
z6Z#M|U@*v315gJOfFdZ+i}1_zrn7**wUkZAqyDZyS0<1_D>Lj%(sco`{~LR5f)<23
z4w~3Nr-+kFbbc#YNTu>q3Ayoq*0#%IcXlNQCZpY%+_1sqm>(HE**khpbzMD0du}u<
zKPC45Ihs%ryo
zV4tGh5!Cb&7$L_-YJ(9tO%xPG)`*fJ&D^ZBIAkSf$#~FjvmgYDW-gv&
zkK9@R1^%~+I~y+yqJxf?YMJZ;=)ZUGz|4i=v3nHXj$&x^(L=}IJgHOoOM>$Mj@AZ?
z;*%Sc|B|pH2o5V`q(RFssYKxt`r5=!&{r2WphAz+Sm3Ga`S?ph
zkJC7#`tsh5HL1kQ1f{ZESurElAS8l-!`^E8dX@CMoaGfNrj_)el75$?nRJ(Qx*-iO
zM_)T8N6+9(NZQEJnjRsND6bt;&by?kzV`wun8ZLudk=A4vAyl+AD4$8HxD%Du(+aA^@^Toph}>k{(V8{u
zI}`E*-6j--JxW3xS|Dp#86i)rv>;F#D|hRJ~z3T##+T<1T}tL?9pol;=UmM
z_$de-)XmkFNDWa%PLl?r%vcQ`hNh`&7FaNo(o+{n8Y4InO)PE5i=|sq@mLhCEKp2n
z;oZ!OZB)6v-cU<4;(u=DY9iZ;u@7
z&CPrBsrmU>OJG)YdYtxLFjmN>!da8a>UMgjrf2>2N4wGHKp~4{EB`_N(UX}VdJSMg
z%qVds0aZJ|rcjjoOSQ=UeTvC2!!4#Hd@4lS(GI?pfM2z^_(B9lf_-*ww4u$%s9kI%
zt08nP7V|m^0)_aMW%ab}GJA*&cC6OZ^q5{Jz2oCe520&nu-aBZk;u~guJO_8)ZkQ4
zS7o4WAfE}rTp=w_Jwb5SNb8U*23-Vugsc^G)i*&=7%U7LgyS|6VW<$GaYBJnUPun1
z$7rU%963EWIAHSm_e`}fb+k`U<;QZh8C#^-_Q2A`@wXh=fA78f_uY5jzGOC=#DBl+
zbIlYIX(T}7n%7z^d@@)`xAZvNwnCuN8ScraTHU#p;zZKoNQPQQX1i8<`424bUtL;S
z-M{P|+q-v+{`;aS-`kQ%#zWnC%|)4Bu{lDdz9NLgH`$L1Q6}N!nh_i#HAxIBp(Iiz
z^tH@R&{u`SB*#|ZaAPcYLENt@LxROMu=tSlROBF%QS$ZQU}#vxYyKECGY*J>d9)5g2Ueqc+}2QKHjwVuJ%jHqNh#x3zn@#bGrF4g`yc<#Nei+K
zc_(EFHscW0O~VJ%_)LWhEsD5(_E4+rD6~3RWEgky$ZxZxNfbJ#zrbA!-JVE~v&=s;wbm+@pARIL<
za7bSuL19~A*%%A84SaH8#aS^!3t1)l46j^k%+N_ziG*ql!&K&k)G(}CyfqvnBbRZ)cQg?>Sh2CA7~w&PG2%evF%P>CAplm-JzAq)oC6Go#-(LK~i
z!4W)z?-3fNN)N(Kyz&O0JX(wIUKk(SyRvWqWh7H$^W*cGKp_*(7jrpkri1$8N^!G~}1Zsx#i137wkd*H71|%;O53~xNPB;C`ujY=g_Kf8-$x0zO%O)@cPEP
zuL=$@)i1)&eLB9Z&^fR0g=Cmlv?uH!-
z3Iai*z+P1sXhj&}2V)vgG(Crm(Pm&n&o77jN8%-lV8mOBk^P_aD69MU2Xr(a+ek&A`zs-CP%{o&nY29r05ln`KGDZphKDS+R{#jG+7a%d22h_z`$R`
zGoD?!{HCzwr;~%RJ8UmFBRTp`i$Y
z+z@mG!SWC%y&yL|J_=Fn-xmsajuR?H4$XvU(MG@h7KAlY)A<`wxsp$nLl6I=E9G%k
z}o0a&V@piXf+sZt$#M2L5i_iv>W`YFZ%RX14&h)%kTV2uiK}E0+(aJ
z4b-iD3y+KlqJz32v_%D3ZC5w7H>hsD0M1+}i2au|zT?hf&*+s4>fd2U(6|GU9o^L<
z^K8j-BoEVXEP+`y*LHR!iM>g~5$o-vEhC
z-}qb8o)^ejmrg0A22pWNwl0YV2rKNiZsF>iof9T7#0kZjb-Vcbv9;Tr6#I)(cAWXq
z*;+=aV#~&k4)LL;n85V{M);`LgEu#nzpb^`?IkOEF`M7zffb#Mch{mVo|){H?K&bh
zWd|dA&&ZB@GSoqaAJ}e{d{88^WLWR-h_A1GPQbZ@Lx!(j`vK)G(v()7*)WyyC|RBK
zC}Q5CAUO)rj-6~w2+{sU{1zrO>_eC0h3rOVch{t1W^;%##KMR_s7s>FK@^k09f3?2
z!3JnYY@uO|S{^ns8Vekemzk;B#PHA{3S%qfpx_NGpEggg+Ak#?oPzPZs
z;Es7>H|-#lQ52Z4JUn~%#KhgRv-gaT-!t1eGSb;GI;!}pkDWR5*uVh&S@l2q%rlQX
z^2{@j;?u-O2vR}#OhkZxTc`jDC!d7i2v?A&qK_u(kkD5}TvUvuSg(L0hPzkBdOsne
zs>n|3vEDKYLQI0T=xH%d#Ckuz`9_NMZkgp3_ULu9d`&q;vEJr!i1iL_%h}2mAZOb=
z4zb?h9p*C$k4moT<->BN_*Oyj1f`s9(^m@bNx;=tU;=JphWsZvo{2V5RbXsDc?w2l
zmUXiGe;LKm2uUu!kbu{eMr4vdOIWY$1R)~Zcu51~*
zIqYaHKn;bJa;`JqDH{q)=&}!2CZQRsNcn8KK}O>a{e+vu^Sy>_Z-Bd^EeZS^3D(}d
z12OS0`8f{wv1Y@Od@2?x+8xfWn~~RmAg^9RBN6}&@|shC7NASOx1e`GpjVD>M(==d
zPvv-lUY#HZgmXb~qgtQ7elv{fw?z6|5Co;r#TFUWYwL3CuVSN@8&J(8+~M*yK*e{D
zZ2NBTtazU!7Q7E06=qhQIw#lI@!FC8KE;fKh?$WgcxE_n!a*hqj!)98s;-wf(KNC49I$Gvm#~rRn@@
z%NP33?{1xPS?#TfuE9{SEm@v4ncT+|*VPZSr=*D=FL3FU+n#*$k!KcKV$MKx;_~9^
zo$Zt3$cJO8DwY}=TgoTqT^wEny|$BW^7EyRg*I&6fH*-Id6p?(B>w6PFXlu(uFvW>*sIM>8Cpe@wVQ%u-h}x(mx#S
zN#{p2lYO~5bZTJm3~zU9D%`f|Y}b)+r8`h5B8gN<#NPbY)e9YyC}5ZyeiNmr6^kR`
zy!yw5xZLDayV9k)!_sc$R1wp~o~qf~ii(M~I~0y{`Q+hSsJ%iBGDnLWc$yl0K*4%4
zo{rFj^GYc+IzqEnrlBa5xpK>~IGvQ*VG8+{R5Bb2x}7NO(A!~P05#V`pd2PZ0()my
z0f;%5D*LH;BjUC>Te@OBU7^FB#YV6IQ7U_}s`#R%V7jd(9?9U~8?}2n`ASo!Nw@DW
z#Jf_`c;UY#MB$V!3V%dsJc>E~meA$|hou-LaSBV3a0hWJpqesCc49a3u0IKkj?l3u
zka=t!^+f`4gu}+qMq|S+G?|1zK%+b|a_y~UG_1#54l6R`Y=nXnSTP-?6Cp!j{NTV$W25S0_5`+l!(6;%LWY)@nA{arkwh<-}5TwKG>~8%{^M
zM$7FZgf+2Zfwf&a){ut*yg--`SR**F79eOh0#U#ZuqL3I`Y=JewvBx?CC56qj8&c_
z4EteB9!1`BnVqa1>noda+ED^{ot;3CR`jN!E*TYENW-8*w<$v+09xnHyOf$c&ZHJdXUQ7IMYgqtW)Uti@+GIf7=Nxu?|G9xaEWBWZ6YTuf9i
zsLp+ph3WpB#W{ara_msMuaa#kX5z8D#bnLR_4gl|Y`1AhZ+bN~cdRx$ms*~?`VkXK
zYXXHf{*j}X`hl@Vrnrm2@kmh58l7bh>?lB!w7`HJ@-V@?;1MycXWF&1s4e#<)~i1qYvQ`UEf^l&{XSr$v-Ae8gUM<9}W
zYh892m6Y^0?uPkcd>Z8^2%u5n71V1(CaWtRPcWbLcqmi9+DKq;md3(jq!cy+k6*lU
zB^nMDTwd42P^QxpbR9X88&^K^GyeMJTryiWcTalUp;|xR=YJQs3q|-fei5IdvKBfV
z&yAojU(&c4Nh-zsG>jgYvPdIR@t}gBHDMOQveB2ySSp!7;@$6c&=Hab5v{iB(Q4n#
zzLOuV|J)bwt&w2S>2=~;yTrHV$NA6r{aoAmd%k9Coa|=|#F%ujKldp+bXD6Gk)le0wqN0lwV$VaX{HmzonvO>f_iW2cbWTVk09l9=6p=KV
z@2qy-F!_M>iIoTewyF!T-xUH(!pSitI2=P!h$^Av7)t1Ci-e=?&{xGNbpk3JE;;t)
z6LRbvVNRcc94;gdHs+vomHduZ^l}+FH#uAaj+7oXJSpOx_bBg&!!^xTYKs#+?S9Q*
z03U0H6#hs?Pz-_t;2;Tw=hethQU8kAf(dZCUWpC|Oy7+d4%J1xc?WNbvPXKI-R*7B
ziyAyE#T9{jl}?4i6Ukd{8UKo3MWLrP7lOYPi4>hKfAS4%;yq#$Pi))7Ih^JHejrc4
zw;=l-;eN^S&B(q-xLU!&a^lKtyq>Rny!Dr%-?0LgN697m)rqJFeRX++r(<$@ixxu7Y$#V8%KQ>
z1Ra8mVwr;GE=Zc=v$UT&oJ53#zbYAAM+;@NCH!UfS54!I@g@8fq#MZDb^t+6?6mAv
zTdwUM?}h&*n$-&U0(A+7G@ZRl4Qr}wSfl6G4KQ?3?j;FA;+4A9+KQMdifcejAO}L}
zci5=*Wna+BEzYRdIS|V(c>O_J{S{Bx;~GVV$!Z!%7)n_$dhaXAU~7!O!`~5pz&M(>
z^~ZZ76`d$_H%;#B%H{V;70XTD*l$E
zh3=Bjm&Z4u27N4ampn0ApPNWnj($a)4=ZOT@hG4g6BivxD2X*Ww%$tiMEcrm{1eET
zq}ZJ@g_4yGZkO>p#xKE@lI?9$Zkz^jQ{D?3h=}%1ZE#!09j!UBv=oc_(s%gMq7x2{
zaEKVvZ6wlG=P+F5nG0fn_$TV$nlDbak(1QE-qm|oH#hArPZkZ0zblryF}+mVbs`B<
zQ{k_s7boYA)6Y;AVl4vGkLZ~Gun_bTPM9V*T%uM|A`(i)D{}NV^wYK}tQWb?Q?=t@9~5aZn7>n_D2hYOtG4XG
zaY?QyP0(E^zEbg911I^gT@Ib$>QN!gCB`$BYhHGHJ
zWJ<$c+02(y>O}6a@X~M#fH;LhHAIf|U7w%on%vA!))aSrcU?vo`|=#yDJ0Z
zX&asHqP1PPtJ3r6W2&?M!J&4UJ!vih>q53t+&CS&{v3bX34dm!8a_MyAO%vRYNNG3fB^8|AL(;fXqKv
za}MB?7-Y~u>xz|1!dj5r7-LpwAS;|{o6WNB2hj^}nyvdFxn`C<4jhvX7tm|Rtu#kR;Y}L-b{mmZYLj#AL9@}Jh
zf6u$#mCF0(^ToZsVtQ#Qk%+GHW9e?6)0a!2Mm&ld@j$|C+o%zrLro#ohzDntf9~nB
z*9s8ciwUG#PK7C4;{qCZI>1=&0t4y)B{5MFh68!
zKJ0a)SRYB_@Mh9j?;;aTWJU^{3iY-bg)!+Ruh|9#^}PiuL}Pg|caWiHfzXLN7e>6_~r8sAD6E%5}R
zc|Mml?MD@0s>f$EWum_B**>6ldwEg)-bz9mjG{|m;?7(umn-L-iPl|v#;&841Bql>
zaxf^sjl0~=vR|*+rl%CmNt((AOOe=jlnG&}s^XRpedmMv&RbMed}HUWU|HIEvk`VD
ziV(eYY&!gvSZx?XnNy=3-#U-qeXEC2`CXehy>{v(PMF-kZ!hk8+C4Wrakh3ADsZqD
zmMn~A(pV1&dY`RL5t(nJOTvP-1{DT^200^7D{E>&
z&DYVLIQ;*5A@WA(PAPA$)n;>)y@@=s4?02Vi%yh5P^rm6pkKWa%?!-kfM(Do)O~GH
z{W7S+%U0R_>{J9@ZL
zp@)ln?V4TH5JPuc+y=`P;MunBEHps
z@9j^&^(~J*a_QLzo;`i)!2JjBpPMCZ*56ym;oitq|0?W!pMSH7muwQLsRo3=x-PYV
zbl{fr8E=^UqTG0(%h*k&?$kt;R7|D;QBeg!*vOq-pEXzB+t(2}-jkn0!&-j4ex`LM
z7{ZaRzM#|TuC`=5v%@W!p-k`oo>+(5;kQ}c8bV$HV{)K1*FM~t9?5s@dA)buGuT%e
z^95}dx7iW#FY#YlzBq6m-lNCrC~xGWMe6;&t23MFNaaOtJP`Cbd*k6~%T5Ey
z9d`O6%}IQfa&~GbI3^D7;$ilEwP)d9xX!G@zqqeWF>^FXDR%NLTqc+FE$rNEzrtKD
z=eh?MPl3S6w{TzYTTt4?Dt!xZ!x{`(WZN7L9jLCXok}Lo-FfQawTJiZp{ori#)PMF
zGI>(>G`zRtX?*)+0h@Ni{n#W4<;UQEgo0(a*F7onDNf4$)Yra^-N$*Oa{Il0E4JSr
z@-5xaCHZ!@8g5E^HPO-5k+5ZZT04{D=!8L9^gq@6A@&E+zH(pf?*03wA^Fe&WJ7@|
zx#b3KTfnYi>lhRDiTKIku);yKTIY}npy)BRRx~H}mxV~fCgSe1;h{at^Sj_19vD7Q
z?Wb%Y87G~x1!eQhg*T!anujtJB9+uPvVv^kq3@^y5eUIIuH
zW*i%IpgR}IQff~rHCFQVU;Vmnj8;eA{&u*YyNkN(Nye!1)jb!+&yQDDBJvcFV0*eS
zX~ZcYJ5$Nx0QsIJ~aVHuQUa0oJQ8cy2p5eIIj;<4T-FXKZa42j>{OR7U
ztNjhIRR>=;uwj3!Fpmd3Y&N^sVPW7WpU=A<<6iH~awOoiY8wrb`rj;TQNIh_YLm<1
zpy(i=v%c3tb|S;g7J`Kv&x)jt-g1cpw|(ljv+AQT
zRr=OXzwOFfpM2s?k3V?%f&1>g=Wg)DGwaWsI&u6c_=4`>CBCRljKdR?jivE#D=3M=
zoc{)Y?Lzd(E!tGy0O#!JoZo(bE4g?iWJ3!$bbUwm(OPcq;*lPkQJ;iqaunsp|5Wok
zxm`JRQsEBr^m;n!8zG&pNk4h1
zi0FiKp@sfn#_mH4L86G;PJJeqAKl9ES$iv?L!BiV-zgL`=j)%nq3}%}xsgMAP<9#^
z%u!FH)j(6#fXh~F5AYvvuhbvx)az1ZyoWs8Tf}>yuTgjNN07A?@A*FVe`-+?@wsx(
z-D((z_FqDtaFM*?2gHr+B%$V6*uV^7(A(AkjtBw9L?9@FU=C_4w6!QgL6EGI%OMd9
zGLknH^pXY_!Ju7@sqom2L80552r{WQ@!UK9$J^ifDg$;FfO0jgxY5=UOu7ozIJy;2V%aJLzxrM-zi_YW$3PuWS6wh`vu>X?x#iLmb`M
z=DUSB`ls9J7?mLx^*yBCxmPN8Ya<=Dq1OpwrLZF)wWeP;wE6HhW#+Vs_CJv@6F*yE
z8Itn8Ve?;g+YFoPnoRtgJYM?_-VODcyosXp{$Eg(N+NQkzubqqJ=tP5+c>CUGlh3+
zAL0!h;;=zgH?$Eo@){6@doR>jjV<8>l3%DhyJIMHZ&{BDMpB6tz9-(=8c&o;zZoyL#uDYS>WsFdQYzn7b@x`o+oPg5
zeqx*1v1c8p1jjH%xttJFiT}&A0K%l>P!r44vN4AOT3JnsU7_2*Yd6~mTu?Gt#hEH>!YYQPQ_FQD9A;pz!eJvJdqs;Y4P@0
zIbgQzh_OAbI>v&z*zCQYipvvBC2g$}I1wJ<-H;bqyn?Hzcx*eiQ2o9QTT|z=u|Ss-
z2MhJh_gTs~XE1;s0J6!H9$}N6tKHEEy9;BCeBY+n`zGR45HQ8wEr<~{!jPLb7ie+Q
zvfgGAHSo6?GwoF}C`tlWVL%M!zp
z+isjEz?YIi1(stXWW%j53%Kr?a7#ApQXm<8!5&{AUFGxt$CGger!(y_XX&DHxeoOXl>CboUF@AW`T)JHZ^
zEUkz#Z<~d1tfjZTjiIykpXJ;BNq-xSQ@^%#9C}+%?Ko~_V@(}rHrI4$1KLhK-g2ed
z(8ipAij_Xgz98TIQu9h1Pj9aDX0x=iN4C#`6}(d`Z+jc#XK|Xi!0We~R;p169qm>@
z6JLduuHkG)3-JFU?`j&TE^aKUByKJj+oEi4w%C@&VmFAusI@+_fdchMP
zi0|kJY8>}-tI_&2&UfS-$3QX^(y_}S?zQ9JCz;Ep+FWQ}Lo7_a}VethUzG6v##SQ1SsmhaEC#REhjB9dsT*baI
zznGJjzuY{h_sR|*{01i_1Rl7pNpa1xrdYHqH~wPkYS9rQKA5)R
z-UVDBf@|b1Q_Z!(WIT_$Z2f{zFc+@OqRcdODENuUw1tKtC%I)9T|eD4j!5oyXT9}=
zLev(zErD=oM7CN#l%tr|PvwvYcQ#%4
znGeqAI*)iCyl?UNfzySj-V{1IUYHN2({vj>w`K#u?B0dBeJ7W?`U2g`X)JH|MCVdK
zv2;!47mq?tHg+Os=eUpYp}y4Ou9*c-b}T3v>eOL#T`*7p}u57`+4
zacM8G3A|DhDmCv8QdvTE2hR1uCtryMyaK2N{Hc}E6VL74|D1APG1{`4
zIlZ^q9nKU!_|CQOna*dUBmJxAvXzJ@T3mScj`er%L!Gi%m(W`u)r}XSQFJ(I6bbKO
z?+~@s9neg`!v?$uCK@!BKAx1g9N!LuW)r?SPZ>uof_4P~zh61clB|ur=VNeokkup7
z&EfDa9P*7`XoEqyM2&6our5isr#A?fgmwf+f)QO)C*=To-kK=(_T~W@hFVgb74xaK
zmbP%n?Q$4dl1I#PbM*R!t@MYU9_$Wo6BfOf@|;<|`{zEq@BR0=2S!5q1)bzTjrlca
zIT9_ep3I+8PQUm2Pd&TR%<-TzItq;|UXa_RzRdL7PCfT5FzE(*fEi%&mxM)Ne4X$}
z4F7(W@F<}jWM6EIHFE44!XY%O4Os1mSXkj7l_*7l0di1AQpPCSy6ol-3;unQSV*{hZ
zgV?0bkmq4-YIYh@5L8IIO}&vC3zHgi%uIC0QZ`#E)<3uC
zBqi4Urmoca2YaPZ?0xWjs_p)ZKlgd5uKdMpw%~23tHU#vRH}X=m9i|KxskG}|E=GD
zS4WTE-_sH6O8Ij=@P;G_3W4$~x>ivsqf%ecNXND}9gfCz{{J
z_(|bU;BC^Mkn0uxMAJC<6NiOAfrFHFe?mtL`4e(cC23Tq^N3K}}kj{eeUmEkZ~E
z2^WjF4!!U*?%PBo`5723{EVLveg@o0>1XI~CqF~Jz3UF~9He(_4D^@utnXpwEXk
z%){ih=(9l^R-p}Fgf_hSHLVaF`ZcZ4i4FBQbc1VJA#!IkZ|s`ZyWZF}t%DCA^NpPy
zyY~sYrgiDwxlGeFt@c!K;konYp2P8=tPP8Ug#gG39gU@KteK2dpU-jg;_T
zxIyL6`v_b?{TmFjk6#E%q4`AuA1lR{R#}g)nDu*etrp_)fLh!*=|GSCX2H~(hf4HU
ziT_O-x444KmDZ12hK}^kylLOq!GPIovRPDd>9L`5XnN0{GfVG(_qHp;PW$U$%DFpg
zZN2-tB6fo%6rekgr|+LUG&aBA<^K~
zhJWwJqkddiD>X`O)4f9|@-q5hP|Rk2lEJkyS(Ga_r((uwW;h&{l!)=PLwI$NGKxya
zP*rgZmGtC>L9Dl!KzeRF&Oqt|hbhqv_A58}hMP=5dSi0SVs1LqjaKs=eTL8x*}lHE
zHpcp9`a}uD$Z%V4ThISb+k3!Ca#i=jRnV1WZR4q!kw=7UF&vCT8afCI+Q@qPBQ`D{PH28^+KzyG;a-P4nH
zSHk88%k=H)>aKIoJ@J3eDHTEd1csQqDh~6u|I@7^I*m$Siej)7vw@R61vc4F7Y*Nm
zKDaNDFiPGD8`|9QTlT5~$!2~AQeuRA*TRRrAaxyvGjK4<8>FfgXud-Hfm1`W0lf{#
zbk=BafYfSsidZ-Jel4H_K;B6o)F|W%ILim$VAp4lHv`BbG^*vEqS!;z?X;S8m<~-j
z1by2GhqVod64rLd3dga7na8HTb&V)>&^6?aNbN|_bL8Ri`LNv~C!<{Dboj=5&s@EH
z-X7K)wZ?2FbUcw9u?MVnm{e-N&bCte=?@LyWY`_d{`6DtezQ`kw3|+5
z(ni+s6>@-%lItIJ%B!-*(}ht2m@&s6WH>>hCc{k&_gKxK43GalMCoXUFh;`WYqc8RuE
zN876dutcNNQ*=k#XQ~@GaG%FJchCTp-QMS0-)YZ=Cq_qkZghNfe5`rU#+!chjknb>
zN#iAK{supB(EdNTWB-4%YyVquyB!CLjD>GNB+qc@gc1*006LxHbm)JglV8-soX2#Q
zO`}23XzJ}|rmPKMLm)*o!+{_}gN=Hv2O`C8hgh+gG}QDMWlR)H>6RsUxLqqCO7XAjZM6hS*3S)3;c2Kp>%FuIAiAx
zVn{^aB~(8D`JDqq`L>Y3Y+t(m;~#I&*v6@#{O`A!0rQ7=M5Vk>);5Q}!FZomRq~=V
zSBKG$S%%_zf%^#FXLRK!LDvXZZ00OLWzWHAn&y_WU8O
z{v%dzuqP8&3;&x|{{yR6SO{BOP5+xj9e$EgM_kRUu~qc;2Mut}#Z~@L`wo*`zajhG
z_8k^kSR2+{|j9E#;#vk|2!6MK6P`7AI46VsWB0-YBY_9DtQGm%20^HpYPf^W`C8ryf>A~
zc5fHVtG!q+G49f(Mxg=t;cyfcP8#dAla8X*YE1_H3A;^cRE08nwN^o!No?KT%*AyEhv4nL}t#6b)EP^8@_5wwaba#4Ffl{Vm(r!XJ}BCFcAn9xWv{zXf6y
zF?4j@LgE+Qhk4Wq@QV5Vs!oRYrP(=XwMO-ks3TA)>HsmooD*YE4Q@G|PS2%L)i^kS
zUhQK}i^Fotz)t%fR1vaeP{RwpP?d;9W|EcG|LkyoVTr~EgJABZ0&%}h9gBZqru9{4Kr0421-t8$Za_|D;q+Rd
z9O7+w?0v<N9Rl%(Kq-e<^rio>N
zzX2gC9q*S`P`!Xu}?z&Ejl-|)@o8k7?A9O>Cl$!9<
zb6k?e3M?$w+1?w9ejgklAO>dgC*>a=?m
zE;+skmf`-R+(4rYec^DB&d?vEZ!NNzY_|^XI&BS%k*~0T&r~#~*T7Z0G^R!H-&Y)dJ=
zHK*r4L)=c&oZkilR5=U?NoxgxbDGftP*}ty1v)DlwLphu+q6u-4Gp0OCd?04#V7Lvi33T);pCGB@-p
zCdUck(6%>x&2slg1daB_Z9fC7AO{fI%F^^?a|FMsw^~7EY$6!&xg2(jK|`ZjsO6_o
zu7W3X$D5(rI{}9Zo@nga=TJnk<37Jp3)$is#xS?Wyou`t?)}^AEHp~%DLGqbV2dxZuqGmR
zQgF10xZ5eNABz?~=
z(1k=03X;;LDtVgwb?E`|vjy@_#dY?xJA~RMApOj30#ZN}*Rz1=-*p2oU_cS+zXWMc
zeblSmRC64SDp;FK8UF-Ln3AEZqD%(HfW+IvZ-M(t)y5F){qkjMj*be0YN9GOM|Y*o
z8V#DiIrlV2SMAdseJAI9v3WQu*uxkoj9#$*^~SY>IO&AkVpZ$XK0k8P-lExLdpCmPJw`S}a~c`?@RoEb{q7o$bqW
z$YOQ2kD}7zww9!!^~IaNf$jdFw62uno7vS^Wlv<;^+aR=&|w$vQs-We_vKs<%5QJr
zxQqCg)BLld&9??`!6Xx|*?5yqXS&MiwF<3XLCIXX;;PXq=nTA8uhU+p5*86UEQ$xp
znu81Ea^)E@!z)cLM21(2XRaT3XD!miFZtGFdL9;UZMw1dSN{n2ZXW5rt<0o*q>CCa
z{k~+iRte+L-^X)HrH%6J8a?KnEwhIVWx
z%XpPqlKJybo!)$M%29~(Pbc$;%E^~Huu=-IHtQh@>43CJKA#R4+?H&4%pCkNv6sP7YO
z!Ygm%d_sftpoFd;;w6s0^^^42a3nGCo|h?H@D8X+Hr#ZO;1YHZ4*Yoyyh;(v26=++
zh_sdcisgDOnHU+WP1Yxi`9yEBHx
zfGK8qgE#|2VlQ?vxrT)_7?&@xzuQTGCBSh;hsl)!<0r0?C)LIH6x0Bm3xuNK3hr@|d*-PpBvI@L&|$
zjggIMH#ZoDAi?irT2EYub`xk_DKD~C?iAqwk)>6Yyv!lE1~y|}FPL(SJAu9k`wAD*On-<5BAm&~;~KQ?;Ggq)n09<@3(!uv2eRnb8+j#=8{xxmvZEn=MohS4S?5
z*QY&tvl8zi2;q|F*P8xXbZN4Dq<8SFaK&X*sq&WM!bp9+TuMaCd7p1!w%oUv^y*d0
zyrb{LRP$WFJs0m8PWT7s%k^m*pDcy}$PDr`^5q!$#8o1yxLRcGrTgK>Dap!Yt15Yg
z`#9SI@oJde($xiqUFgRabYETOeuHg+4*dTLOX%g3M+mZm~c
z3_xTKhz;zJh#tYk{Efv|m)=%8QX0BE6E9o+zH}gv3_H~^U98uC;_y2x1Mbpl$r`YP
z@-bV5g%#c-TuCNAZ?4YuR1fv&)W(7@lA#Hu;EB{+W$mKl%X)3*@>Z)7V~}|s|LWuM
z&wlWyz>#o9h4T;xRj#*L22=}8{5atTC1u-La~3<0B5j~xmCM`WmPhR|C(D?VB>*HG
zp*}@f&-hJzn*DlB3g@ZB$fb)7RkEgh);ieq2ge%bY~GZu48^FSq)eW>a50;^c#m)<
zHEZ(}O|yp+tz0^d)Fyrv{)1VS=`{aGiNmwzg2y(S5~BsugMbTS%n(*`kKO#T)Y<*Z
z49zV*D_Np+37u7j&Ip37Kmlo$*y?W>TLr;D-RouU>x_YHV4$Z!u|9HA5gU*}k#1rJ
zyG({-3izhPAh@=VtGZH_EpNsmf(yv42Xa!3ZleuPSBgeV+7M)xWMtPH!czz%SzYea
zL*=8LV9x$s*PXA_B{&Ep%|82e`15f^ha
zKODI@JbdrO@05Gp?HCJt7$(-z_`xLBSUX%!T!)J*&^quy6!n)@iGbqOMeYV8m3TFs
zKykf5CVuC7_>0ndn)`8SeTGQbS?9SQc3n@>K)g>@%9e`vCnpE%%*puz*Ok)%8`BkE
zL!x?;`+Cz*NC|>!BpC&4lnMoL@gG?u@*;^(n@P5*K`p@mP5mkIm4YVOPcVZZ7D)zMBia##~)ozn2R--$br1
zT>)U!^)4>y@FN^uiNEsP+q?JT5*UzQ0ybIf*Ei3~Ed`A0AdUoakQqAU#?0GU)uF0wN?8abJXOVi~ujv6L9{5{qca1O}WQ{))eG|rdDgk@x`Wuw~b(k
zmRe7H`JVA5=mdN_x*UHG{p#Pye&!%I+=M#hg?!F~v>o+e5$4sHgn?v=u!;6>C=fvo
zn`gflrCx)Hx;r4X?L@iE|k3y;qhP4ST%oQikVy!
zk^GXY$76lkanX}bcxnM#^@T^%ZtGj@Z!~`x5B+sqgokF?YIUtHJ|tC!;gO&x@Q`nI
z@$s3}XnQ4IY@woT$<{j3MGoX**~2p)9Ts_%&(V%b>m=F4^#TuZ-&rTgCa$Mxq;6-O
zB$&9KqO)<0d-m2eiA)ciU=6vRvw4t7-IKcaLS$cv4?5~d|fPCEG5fqS^x(pd+JA-fN1b@p`dPcvj#t%HzxsZ!Je{=lR?Lo+%|Gto|H9MfewXU*q@^C1bVK(z
zjP@4d1?XN6$~z1W{%-eWW-|k^`*Q0(E9U
zofzWbf6;XJ_r={PALkd*5?IDB1d#!pukb1f-iD2Vz04_Zz;=c?22&JqANrfw#w6Gw
z@HWX{W|1EX&qQ{9gl4qhGrKM%asHz2eFwKCk*>Gi?-=)3#vBe!=WN7eT}K+mpUb5J
zo~eDNWg@zx%aTG2e#V|~e=_kGHjmL)38mJv&ZO4*HuuGlB9*`}0c-KM45@^2MwKCv{JC|oFxw1v
z6ec8b%^QnWrZfP0ZYF{8+v5
zN=WG!{^ya)v4(Xj0qz9irgrE8>|5)29511}#Kb5`mIX%s+-pF7X_YjNxLPD!$gWj_
zEq@N55x6pc55^0875I!{?!l%p#w+B*6*B}n{2v%fRK6EPpp}5Lm^}&^g!y5YNb(3<
zWJ?{QSp)I?F2I30-not!4q-wm9HfD4Iuf#5je7J1DD!2tj?Hl+ucum(A|~yCNgK{b
zgxjupr&WrxFed|_`TrKjwK_Ok)>;fWUC&53IXK%4R3jl{#9(zMT)s%q=^dDq*<5~~
z$Bx-QUmcqGd9%S_GCMp@k29Ih6&zZh&00vPHHx6k9SFGH5k!-pQJanCq}v^Ifk-(J
z>1RPCn$x=~62$=qCZq?4Xp!Us9-`Kv-tKj<-Erw%84n8B_KflspMQ6zijjj{@V~Ea
z^!9F4#eajjTril;3SZ5v4hoqJikLpFiWM-AA?vc{EixW2c(^mx;P
zFP!8?T-J-@)5)q+t3vjeZPAYEKPlQn&TW+9fz?XKEv8-GT`VC4m}96-4D}>}VVx`4
zH{|k73>mT+osgPyEZj2|tiAf8d#n~+@rY5v+NrE@)YVwe
zk;d?`A}}hE!NpwT%wVT_>|2sMM8}E9b)WD&$OmMBL_QbDSKu1WUSx)4b6D;8!XoIz
zyPZe($tZrs%xwj5PIQKXK+qtj
zaTVLmF$VB%qv(8e#shQ#&63;qeG&6g;0pKC!%vCNPEmkl6dQZ+gIAs40oWb_iggvm
za}_wYyB<7!JQ7*)m&Q{X*_JzN9*go6xi|0oSDXDO82jTONXKD<
zanLbvj#BQqzP
zXoMhyh2;wu4jt<0;kgU1yYR@vue|@|mo6SUf9UL~lP8X^ul20@{uUw)~
zXcR4c+!&?GauXh}ij{D(aB`y)p-;huj>T%C`5Sw2pgQtc*zH`7Sc=(i*hQ2p(;iDf
z9df^sWyXy9s&IMc<!W_-Sn#umw
zSHclJ;qO)osb#|9BDU>aP8C*KZ%D^1@tNpoZv6|fDYRWqj{R$+T=Qsr`tee(&x2m_
zMu$xkFw9(;;a}w(9(XJp^-d!vqk{$g^gI_qo0WN*V3+p*My$nCDh1X3&*r3y6
z2!j_WQaZo_Leqi(QJu+fTxLEqH93K3bgf#+WpN`B0Buy=9ssQ>Lv=@&*mCbz_O4;uglvVt?8V`N3j6HdX)7Qu(bg1~D4
zAXmfVUI1KIYZ8=baDmL9<`TgAL}xul&4JADz=_IWNuT&PYZe_=FAojHq8vB0IBTi4MjFJA}8~0pVpx4N{(Kk6?1`6iF2C!5rIz=*E11g*BV{@l88ay
z1V?TxIup0V($TSGe-z^8cW%2Et>^ZU9@d?5gCq%U1`${GtMZiG%NJosDTOoG6du8n
z?+70N(dW4vP168M&p~VW+6W(Mpp;Ee${9?W<5kLQXn8>3Oh|xk(}U$*X)0Zq4ALxJ
z%PILAx4#%(+5CuWDxR(rj8ogCo_CZ@bZg}3E~&Afe*OUTGt%zvwL;;jKb1BHa~faJ
z0F`Xd&JJWodLqGiBB(SdZmpenb2Zb;(?9=#rw--Qv24&ACfMfmtKRhs>pL_wP>wR#
zfG|+O*^-9-C-!=by(ZAkp9){a`{#)G%%H{lT$Af-*0OOXJtWFuKq2T%P4tMVDf|u5
zfe$sg8YOHQ(YkG#{fKs;ISU;q+B3pG?GgjQ>T9aOy<%hVfmrO?z%8UhBNS>JVDypw
zLeNkgdtrkS<9de}HyAOz?6nW?Ut{k-jpr0FF7!LXyFr{ds@-1G)PW3q(t`*AdA(Nf
zWs-3*VoIo7Qz@^Kv&+-GM4tEVWJ>r`V0ZCC7(9<0bR`>>IMfdvmdBTd+?H25G%5PA?O2;c}8S|2R(
zsfqazi;lkVUDc*Hs52X~(QqQ@^Muv1bOMehqa72ymqAOVd;t7N4dI_dr+p&>YmRZp
zn;YdEP$ha^0~`sbkzWG71kD>oj~M_H8DV7pN%!wd`x%D+gt_Z9`!9L_8z69|_OPKSz@Z|b!=OiF>TLh;Kj#4mlS
zPr++Ed6U;q=df>hOv7Lw{L3MLOrUyx8xN9HBxh}UYIzQ=+y<%{uW#j>OG5JS^{0>`a+gXF`5WCv6U3s#+
zspJk09XW8_Gy{WBbm9#4w+T`S91052n2}5tfIpaNs1r>?8ecDp_cI(hN~>sU0V<%*
zq11yhPhptws4P^uMVNUPK8x^6Aj69zM*H}=b`eR=%IUx`i}}Kt0;WZH)0^3zyNdZm
zR%t{KuR>zh?f2_~)!}MTgGk(5JUj372Q}d`7PNj^D6H%@TVR)g$?lA99cl*o-Q>Kv
zAm>v!$G#ROLx_3#hhF8;OL-EBnA0`+d-ugqti&6061rD9G
zj&Fzq3Y)_X6nYlX#|q6wlNPgzMpL)htrJj)BA)Ui?8$D%m4g>`{&JAT8{*A!{8)Qq
zvZwdmn8xmn!NQ)6+2-e$vNbyyT_E8gh*r!3_rP;5od5uuBp+gwfn>fA_51AD=z3UB
zu+lM~C8&ed7~}AnCvSDBfDJ*ijwXFfjKc;RbTSF?ewPK6gPm+dwbwIPmB^gcuNF
z3|-dHz_nQHf!oQ4*2mwQAC9VA%J@)z57XZ%$G@;*jx02r3lYg4qwJHVz=iWt_Q|0FT1AlnRbX1$3K6#^PIIv=}3L(SPx>h^^<*>94C87d?4t55Lp4g+cquaY4ZItV`#jW?yv$1&fLC;1p
z>YdLfMh;k5k0J+XD)7x6k7(zm@N{7O@{((+kY*hl^3|!V+J!*pz?+8xbBp^uYNT&I
zn_e18w4YU&NNy}dd+m!<;|Jq_tX$c%YzvQ*@h~@$l5KAzk}5b2s!fx@D5E%<4t4}K
z6N9JNZc_igzTFCAoowd0z1!WL~IzuU{|
zC{F@5d`I?+AY+8PkNZ&5bP7q1Y5`uquo&eHl4IJ-X=RwMbIpjuA=4sGsaGTUZrH?h
zDDs>jVt6=CJkJD;<}4i7um;kmMjw-5>g|jA-0^0N=kCKmgQM%qi?h=sn864?As3GY
zJQ%d0;v#&=BtCj0lYPq;nWW;R-^J;`wZgs*6+kMMtX|Q
z#lNu)oj~~c5_h?I@yz5%6fKF-&Xki`uzj4_##;oliL5)V
z)n!Ds_egP@_o;7%@ORyN($hnG;-4DUXHq&LJpKbQI_z6b0~#ZF+yDfwwnoJ_SYVFK
z&$j3NB>yJ4s0lI?y9btW@IENv*2FDj@QC4iglc2zV~m7mN86tX9lXw6L}@y?n1x+$I;|@T_eN
z^^eCTI4gD$tB(}a-tLVyWG89A7?20{Va3c8G{G?F`4)+u
z*P1UM=C{eIRHzk7kW&V7%65@cM&u;rfS6i`oXlF1%Nb5J%6=TL38s6e<+oD^i|aB#h5+(w=J~+Dg}lXbLtPrmSBI54G#^}d*RtHX$e4(~oWBCF}rRGfzR(0g}lg*(|6`N0+j3Z@^&6_2kIW?=Q@D
z9R2jXkL99`>9JbaRLlp@RC-VPb47EccRX!i0G0gZ%lX{p%jESxo2dHqI;+8LGdr5K
zrzEdm_*u~yqaIc7%NUdMObOTI-%J!7F9+nLrl4y7BBQaJr>4{w}+nKYZf7_gsF@krVH`
zc<;N9$PdtnU1rR;AOoHR`&H05WvhADSX1DY2(W7q#5C|ag;Jrr#widy0xMpm%)JUa
zkIp_U6l}&8%Vn^?HCv+jQmq2bzg%6Oniv_X_u{_!N+L-CbYqu!*j8K}<3cPZsaC1C
zCA?EHLou-fiUdMmH8qO9gxX@t(CaK(xKr5^rYDjTQnSN>j!%
z7f1UMQ&Zqj`oMLQAYPtZ6%`o@`Nps2l)KsvDdJJcY#7Iw?3h^o#kRU
z@>DFqTn@?VBjl!VZTqwa=9BK4d%vS3yS4Kfu-kuLI(2vAXmkM|%v=@#x})3~6dO48
zV4dTd_G^a9eksT3el;otYXrI(l@tzfrH0
z3;B3767sk#S!)&@i_Kd$8wCdeP?bW+EC(s$rQ!moh;FEMU=Iyu(N?ou`N3I|L9;S#&?1%
zT5tB=)}M8Q79<>lQ8atCt?#u(idn)-bSFXi8^)i$
zYsr_B*qs8fm>SJ30chi8MZn-#fxCfBCLC|@Jrzo-F;rdz*P&jW3MB!qWBgv0qr)L6
z?en-TMjc#CaJx?8_HJcP+Ng_|ZMn;$&(vdvf9##N$BywQpkOZF8Gej0_J-_Rcse^*
z=N8~nJ;wcA(>aaPD?4#qP@tubU_x!&3`}(b-|?g=5phI~+j=_zi?r3vC(ii
zEJBPV6+3QuS+oz~I@NzE4v2Dn
z_;2@tfTtm%#$i%m=1)`f@B0~bij7l{xK;jOeIy;|%D;29GPx1lkIf?Djz3)c_u+&{So#qo=C
zi^lyZ{EN47h7gKRYAja3Y4en=}282z`OXZvi01Zlx6OyvXnXJ5-H<{7J
z5|A#mw1c~Y0K9<|7Xp=AMm6jK-;xCagQe$+_=^*syt9vS*p%G*I>)VVtZy7Sytcf!
zFgY>al@ShmAu-LIku%1aYMO$(*#LxGJVLkIjXMgse4F5xOJYv1^|_Z2VEM(d#qdQb
z6LPz~*d5s3KcsRK6_LUw^FDtGwwA2@8fPg5lk2}X9cXSX8%X=n7jFViTY5;tr
z3}z!BG%j4j$-rHB?jo+0V;G$h@QH3WN{!=_g?7WWsvEbz7_F;Fm5Hfnpnsz1&fvr>
zSPfHVWV(%^?R*Zimr$!AXzY3KXA|GF`E~AED19pJ
zNgMU=cVCOh3V=5R+=(w!P-``vV+8XIvUc%$he#oYnfh1#d&J)-ggI}?~bhcm$~udnasYx1zRT&|eS0tGZx
z2bmZ*!{Ak=ZX;Umh?yUas|NFz{6&vF8`u5bOJL>?OwD@0%y#eCu=7`LBUtW?nYjXd
z*I!}yoHr3Ov-VB^FX1l=0-)LuQ5Vs7pf)4|fZBq9$RG82g3|>v*kMj7$b1Y7GX7FgKeyTzLo@BPt!`<$=wc9fX
zvxIN{fd3BxMjH2N{Ca`w!_x$0R=}SzIkP!vF`MA+@QX+j&(qVDa#Li`|&<5G35(-uVfwW88FSE*f
zMlB{Q5@rfzVDRjG*t&E&JAD7rv4=;d-}17+se3j8;y*-^vU8|4RdVn7C7ZVH8nZImtx$R~Om%ZQJ7tBK-h
zFxz@A?~m3)qZO+#K9Xt1k)v>6!uEVN2sr|Co*tI}bv$g2J0;dlgs=c1f>Mx6=DO0{Vr+u3r#pITaNYJbt9DUXVyQe4EA~2D*x-u^w$LQVWm;&
zcl8!MCo3?q*fTD~Vly5=o}RPWW4f`Wm_Oe7jWUh~@DlV~b2;{0U}yGRvO!>J-v)M7
z$^MOd2TJhT&-*O&%drFMMEqi%hy*d&TPL#feeqdSTe^ezr>r7jkv0$$C**(vPJd<}~6c3}ic{f61atP`l`{)QO3h_WO
z_Ch=(OhLs4;8FTpvxWY=8jYWld>xzz`@5?`q#(z>3+s3EVZwVjLDxABd8F~C3k|Al
zcRL&aY!SFXdUBFZtXK+X8e85j8v#eR%SKdC2Utf+iJktoE`@X;w*P)C5GfOmL+!2P
zf{m%(T*b%cpnWS2K&d8blPmPdV;5uEa9;0qsbraqI_7fqgyXePbXJgqAzII6gRv$?
zpAAk2c|I^Th*lLKDmwt7%)moe3eg^!u@D^lm1E+Gkjub3>6_o-zsf}6U$e+LdrkeW
zg=g^0O4;|%wK`Y*3*zax3aj5;C82M+`2*R8U_(;qGWQ1V@#dpA2A?;WZbmyMk5IpvN+9hlZ@jcOL`3T)@=g&;bYZo
zz&+i2FXe~seZ#$vz5dbHzUI{ryyDu+?z?bqZQ*sylQj}*sj
zNF=Yb)CS`1t`4LQ9vnI1;LQkyG|;EWk%YviEy9Ocb7qL?5n84u4XKU@M|f1x-t>LX
zwD3=bR3d1LI&^wFd}?Dn$ET+H?KY=b=kZx%=H3CL%H^^T<|{S7Ugs{n@Ucw6tu^@S
zmGp?ihtK#PMc$BDw<0ebY{2HpmRoS!9;6Y=;`
z>r?E{$yh?tybH1T^heA;uykZjhb2UX#FP`sqTTCFZR7?q(LYsopqB$vQSD_XQ1Y*{y
zbZX6+%0Qj^405&0XKCc@887;3MT2gnDP1wI%HlN0Nwvlu&WtZvjfS^XtH{oQ9NCkh
z%v~Rv$cx^Mna_zYsL!yG!9H&`e0#6F$O$xeYm
zhkPE)Zh}3wp?kwzUvJ4}tw^2B_dzm>kz_dLQgoSF)3Ca?q0J4{A+W)9!e@K|oi5;W
zOvlrUjzCcVDf1_d$wYe4RWyfaWL?Nybd8ta>Hm3i!V;ShD0>-+w!RRFZjTCyM)(V@
z692{4r;;V7U}qpVcLl9KrIe0131`w7f#TUC
z;f97t#tn@Q;aDCZ>BMTR{TV~t^xH@}L^Onx`$@Xr-$&AYw)J-}TG0JphM>vdAgRc(
z;!%qNejouyG#TK^%_72|uvFn%iH0g;0L;~7QXW?ia6Kf|@8lpb?`mH|MB@F+~myHF^O=i*CypA(!enw1?R21wz2*p?A3(h-jd02xF^xD*z|~SEX3neyN+a}%#SjspV?T)
zVik`;=g_-t7TaKM+aCAcJS$wngEzUCi%IJY;xllr03Xo_Divg|*gJtbEcwJ2+XWC&
zM3)8R>JI!s+ZzVVexSd%Ose{R1ZofThWCx~P>mcDTy6hY6x<}1qb24A2M2p&rBq=q
z*KKw(t5g(VWSVxU6QhCOgEJwS6!F<*h<0Knq?RFuWfwu6qufGsc5Q4hD_6|#F1_(D
z?J2!E%B>$-U6`F1Zw}F8=X04jK#G2!#vzs7>`+MJL%*0uKY${#i=v{6B&Rdk*>HF!
zlbK4#I+l2B!W?kcFQf6r4ux>Cn`wsI5_LnoT-D*8NBz5oBGqC9;sn@MUd6paCDYd>aS?+jN55+QG-RJBbXa^~$qb|Lb>#lk%s!HHC4
z-WCfWP6I(D9m5Ys-SMHw^sw#FaB>z>t}8fAf{A|mPvNJJa#uuiDJ2%eT75<#g@i&F+1!lN_p
z+d?g_+FIz2xBJ$f1rKJhx)?K9;wB+69$?Ji9f)=_1K*1JLkZhbB06JbPW~Li{365e
zB?u((F23JoiWBF+WtZW|a3{H6`sgaJmkMA;IW&LQp!qv!2z)*~FSs1~Em)NPCHA*&
z3xH>em2960io~hTBFO_(zOUJ&I|yq2#!J1$@g}q_e%7(0fB~#5Pmhyt&rX}ETfW=@
z`RU{x?D11Vt!-dQUe6c~-`;Z>7*A%GA;A!B*-vz
zi3;4>MPC+!nQ2g|=xb9d`dVEjHeOv{U0*x2A2Tyz+8DZb*CA#T8*U}KZq3Us$xHS|
z*ul2ej6!bB%?@^OEESnyzN8o+6y4_ONZV2)+5XAaAMYUBpy1^95qoW6zMHAP&OGQc
z0?W@b2XmTP^pu)_gUMweBhNHXn_#h5$^hvP1p`Pppw&ClmjbTv5(Q{U3CU|`$N3d@&sS)Lo{tM>w{KbjkjMSLEcMXTmAd`8M+BG@O!0O13*8@!7R
zy&Eo8QAn|;I)osLBufJ$+Fp@AKIU?`5nM|7JYcrOa4b2R3@`d^`qIka@P(0)D-B05
z;54}OMoTK>42C`Cq>Xp?2{yCYkaUNVF~19)3{9mAYa3_N@tP_=T^M}m`0>|{{iVgK
zx2lqPi^bx0W#f>L7+JvoDF{Ztl2;II`OnB-8~1Mnv0h#0Q9O-RuOl3Cnw$oMB^`8xW1ty=@DCn2`A8FOE>doP6hAYf)TwhXyZh*U
z)u7Xy@eD3_gC-1*_>!fVvRDlkP|j>Mi4kW$46`^X`(7_KZU7ts?)(1>Z+clkri)%v
zrt6Ps_3v*^a({(Ms!E<(B?`>hT#}h^xQA)#0PW+>XS?wcqCN_vbvGoS*$GSQ`(WJ^OII{_xqe4>gRt
zb6}F6`EPdnzm%@rb^7I{(#ucZb*01!H@}Mx;Xa7`3edN|_pvb;oeQE-*R#8_1N8{Z
zHD@&1jzTO^XwZj>)P)Zl;A_=79|OE!38k*!lt7|)m4$SNFghP@zSyrrcVj=RB3Y;|
zp!Q~{UZW}!GMM1!i&Z2$9Ni9jLnJgx^r}GW&?zU;DC%Mv;vgO6%`&Sj?<)AnKDJE8
z;`3I-(8IZcd3eDABN{zRHcmy-XAXtOQ_=b7JteElYBKco8^YF%TWhph4aWSo@83}!
z$0D9cZ)kiNw07{B@mOvwA(yrO5rZEs&bf3VjQ%)sw?A3*q2a8KjxdHMYjA|;#IZRr
z9c3Xu>>Xhd^?B-fPR*&bYSkqjucbOh%|)DhfJkKMo=bp92u<)Y6`n#`#`xv=`T3>!
zrNxEW>B)(aVbBd~C7Fxq0X<#sKE_0eSQO>I9f?WC3;nm3#qFU!4m{PQlmEDVs@-wd
zT+I>>SfZgo+-5O3)ds6VF5CBrf4=ornW=NcKfUxo+80ony;e^s>^6GQElnl2n{8d+PaYdj?OXkWdT?0N3(>e{FDQxEsyu
zXL>6#rJ@SCe%K|*HL}HZq=@(hf#-OGfkRa}>Y!LNusU
znoTnu;3bRJ1Qi2$-yAIz9B5&4>I6KJnbD2rMq#8tA;uyCNfCSAk;f0PS>9%|f@RJ_
zr-}hXlw*o`W2(WRR&w|+LnTOHg$BW=YOV&PoTMP=LI!q5agms<#J@O#yjZG%&gA@C
zKOKk!&;0i#YjtpFz#UKf-q(8jHsqkOx!ge_d@Sr8oRH84{1@tdMzxcm3oCau99EYI
zXlOmRmhC;Ctm~cEfBV~r1fu^Ba5oNlI`Q@2?|=#dUR4*?K!uI@<$UkQ#a&S6Oa4U>
zo1pRrI>}7ldUdp-_50r^G9CkNVF(!a8c~p-`pnBVlkTc??+>P5_
z3+YFASb2{ejdh|C0xH{XQQC1^bl1$)+rhhd4v5#=LA-FPbm+0PDW$>RikAA$2c3eT
zeBIqS0JhPF6%}uCUO`HR@ZGbpgwbC~egkvA;0-Gt=`_Yihz=4?hRnE37D1sB@P1n0
zaHvx&lrSLQY{|4~
zwFf7}kmyd=%J!kyi^D1Gf$jW82aW^BzI|wZpm0VUu^zq5;4p84elmM8LeqVAtJ`Gq
zv#!~y&dJ#yjL@J64B$(c~Rl929^tO
zCs5y^8%0_FTtQH(+f@X8FhO{jBN*F2O?;??0JsaanzcFu?1BX)8M09PTZ{5ltv09y
zSC!iz#R{)ozwHeuQ0ZrBnG?Yihu5jZI-l!cUv8h4;qQzAwgsld0K9h}7zW>?aD}_K
zv^PLFuI(*#xXpS)JP*{D&E_^418hgTLz+86e=n@R)GZ=Ad_#CW%uFLx7kbTdAN)8J
zFF@_07%16!GiTz>ri&CSqe?cL(b|rc0n5yD6fC1t-zoGciG?0_`awF~)rEyZ!DK@J
zrR4=cY6|lOG_RFRprR>nVmPIs+vRMRd^k{zxU=L#)(*t&@ny(Eyn{f+MRlTCUKTrH
z#a={eA5|Wa3-wT97R|PWUpc_M-xH*e%zmW#Vd?+i<^un(fO*(391J#5#UUu<1)G)X
zWramOrjpOWRFrS3XwD*z2c_I#L~zV*M8?)`xA)nrmJ2!SxSjB{=gD2^?%G>(*~osD
zC8PLXw|o-|Fr=EKIzLN&=~h$u6>1@}^@p~I=_mDW!^ccvoqs4EnhH8&dfmgClb%?`
zU2-@w@7rBNF~NVqA&=%-cfo%CWYQ|m7g2gbt>4vsf4V87`68%UX+NzDt(%6pH#co{
z4595&RpqE6xPqd!-_ZLUOl->UD
zOCTZw4>p+5i!u|c4{m}abc)w7N@)8OBesQOY~&Y>T7g3c91~~cO-2?fBzL+yRLBmo
zmcS&aOc4*_LC25)C8$xMVByXh72A(^`uOiteadA&M)&37x|J7k{7{$Tz0e5Sz2n19
z;n!aL@dGVWH^0q)MX-XF7@q-FkWe@H%f^K`)S;jXMdYpRIusW2V(izcL&0#7kR!CK
z4uxu;8n84Z?1LEXgMY@#84!Zuf4lXwzch94=;*yuQ}>OG+&9%TIM`Dh8sc%NZ#EDz
z5CaDfH|!5R`Q$5K`Q(!iA(HfcZ2p77-LS{sfgdc6B3=~Ea3g%+quI`sk1D4EE?Nca
zRvh+$5_3V>p80{g4PJeQMt)$DjUX^X7c+{bq1dVRXW*=2KnQA>aWxwdQr{jBB3@3D
zly*-Exz+VFP^ovvFO%Qk?&zo8`PX2;l9;6$pf>S#AvbjyHSd
zD!C`1vlO#|lRX$>C)TZn7vkj&fBLj=CcEUQgy)YYwZTy9zC;4UKv|0!8e|IM#?W#G
zL=tGU1yNVd=2!hbew1d4L1A4Vi;*$3P3_yEoP~S
zAR+320G0$9=eTh+H`<<=Vqmk+_Ov)97P-@zDP-LV3t)_{9;W|NZpJliWJ>^`vFwKTjvGUe&MAV#M2J^94TCu{>}ufJhTYRC@8
zlK3jmPVI9Hz({FE7J9G#*!h`!Ej;K_ep~;9m#*X#C#{cQ^M0
ze^=ArTM*>R{xl4wyNw1kl6dgt0&kWtR*M4F!;=IULbGg~55+aTU6AP@17zG~t5J;*
zk#f^%(>Rb}sKXjJnao?9+1y~>IuFbr&-Y_ImB_UNdX>U1mp*-I@25@pYC-zEg}vy$
z1F`gv_A8n{!VjQ>(0lG2xpL>8ICtZ9k396s2d-Xu*#-3KJBA$b;{43SIFj|jO0Xv-
zBJX^OBvL}uO_y<&m_uSyQJvJdM26Er0cO^f#O0vj;+Q+hyauJxBJhCj+N?dvMJRnN#@JTwEbO6NqcjbM
z;-tL&$d!?EjphRj&pcXP@;H5oa1fO}+M)33T)C{U>N>MvYNzvm-4eS(@4Pd)obWS)foY!p(>yK
zn}oJKL5zS=mibb>UcN0)Pl`I(yPC<(Q+sdJ;=AT^nN=QbJX&vt6p`2{%xCdIynQ
zjk&CcobFU2Q!+v|uakTW%DS30QLs~#ebmrlw`@e1<<@-le5@3=BqsYaYOf9>ATa_9
z^JxO5gx0Ham5PV>&&G!0pE{MDOwRh15eU3cIMe#F$Y7r3B;peEH)fr$t_FJieAcFpBuR+*Gb@r1Yq!yV;2?G*b|JQ4%KICPjdI*a@c%e<
zSbqZSvV^RTj*YJ&z