192 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			192 lines
		
	
	
		
			9.1 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | # A bunch of tests to verify MultiServer and custom webhost server work as expected. | ||
|  | # This spawns processes and may modify your local AP, so this is not run as part of unit testing. | ||
|  | # Run with `python test/hosting` instead, | ||
|  | import logging | ||
|  | import traceback | ||
|  | from tempfile import TemporaryDirectory | ||
|  | from time import sleep | ||
|  | from typing import Any | ||
|  | 
 | ||
|  | from test.hosting.client import Client | ||
|  | from test.hosting.generate import generate_local | ||
|  | from test.hosting.serve import ServeGame, LocalServeGame, WebHostServeGame | ||
|  | from test.hosting.webhost import (create_room, get_app, get_multidata_for_room, set_multidata_for_room, start_room, | ||
|  |                                   stop_autohost, upload_multidata) | ||
|  | from test.hosting.world import copy as copy_world, delete as delete_world | ||
|  | 
 | ||
|  | failure = False | ||
|  | fail_fast = True | ||
|  | 
 | ||
|  | 
 | ||
|  | def assert_true(condition: Any, msg: str = "") -> None: | ||
|  |     global failure | ||
|  |     if not condition: | ||
|  |         failure = True | ||
|  |         msg = f": {msg}" if msg else "" | ||
|  |         raise AssertionError(f"Assertion failed{msg}") | ||
|  | 
 | ||
|  | 
 | ||
|  | def assert_equal(first: Any, second: Any, msg: str = "") -> None: | ||
|  |     global failure | ||
|  |     if first != second: | ||
|  |         failure = True | ||
|  |         msg = f": {msg}" if msg else "" | ||
|  |         raise AssertionError(f"Assertion failed: {first} == {second}{msg}") | ||
|  | 
 | ||
|  | 
 | ||
|  | if fail_fast: | ||
|  |     expect_true = assert_true | ||
|  |     expect_equal = assert_equal | ||
|  | else: | ||
|  |     def expect_true(condition: Any, msg: str = "") -> None: | ||
|  |         global failure | ||
|  |         if not condition: | ||
|  |             failure = True | ||
|  |             tb = "".join(traceback.format_stack()[:-1]) | ||
|  |             msg = f": {msg}" if msg else "" | ||
|  |             logging.error(f"Expectation failed{msg}\n{tb}") | ||
|  | 
 | ||
|  |     def expect_equal(first: Any, second: Any, msg: str = "") -> None: | ||
|  |         global failure | ||
|  |         if first != second: | ||
|  |             failure = True | ||
|  |             tb = "".join(traceback.format_stack()[:-1]) | ||
|  |             msg = f": {msg}" if msg else "" | ||
|  |             logging.error(f"Expectation failed {first} == {second}{msg}\n{tb}") | ||
|  | 
 | ||
|  | 
 | ||
|  | if __name__ == "__main__": | ||
|  |     import warnings | ||
|  |     warnings.simplefilter("ignore", ResourceWarning) | ||
|  |     warnings.simplefilter("ignore", UserWarning) | ||
|  | 
 | ||
|  |     spacer = '=' * 80 | ||
|  | 
 | ||
|  |     with TemporaryDirectory() as tempdir: | ||
|  |         multis = [["Clique"], ["Temp World"], ["Clique", "Temp World"]] | ||
|  |         p1_games = [] | ||
|  |         data_paths = [] | ||
|  |         rooms = [] | ||
|  | 
 | ||
|  |         copy_world("Clique", "Temp World") | ||
|  |         try: | ||
|  |             for n, games in enumerate(multis, 1): | ||
|  |                 print(f"Generating [{n}] {', '.join(games)}") | ||
|  |                 multidata = generate_local(games, tempdir) | ||
|  |                 print(f"Generated [{n}] {', '.join(games)} as {multidata}\n") | ||
|  |                 p1_games.append(games[0]) | ||
|  |                 data_paths.append(multidata) | ||
|  |         finally: | ||
|  |             delete_world("Temp World") | ||
|  | 
 | ||
|  |         webapp = get_app(tempdir) | ||
|  |         webhost_client = webapp.test_client() | ||
|  |         for n, multidata in enumerate(data_paths, 1): | ||
|  |             seed = upload_multidata(webhost_client, multidata) | ||
|  |             room = create_room(webhost_client, seed) | ||
|  |             print(f"Uploaded [{n}] {multidata} as {room}\n") | ||
|  |             rooms.append(room) | ||
|  | 
 | ||
|  |         print("Starting autohost") | ||
|  |         from WebHostLib.autolauncher import autohost | ||
|  |         try: | ||
|  |             autohost(webapp.config) | ||
|  | 
 | ||
|  |             host: ServeGame | ||
|  |             for n, (multidata, room, game, multi_games) in enumerate(zip(data_paths, rooms, p1_games, multis), 1): | ||
|  |                 involved_games = {"Archipelago"} | set(multi_games) | ||
|  |                 for collected_items in range(3): | ||
|  |                     print(f"\nTesting [{n}] {game} in {multidata} on MultiServer with {collected_items} items collected") | ||
|  |                     with LocalServeGame(multidata) as host: | ||
|  |                         with Client(host.address, game, "Player1") as client: | ||
|  |                             local_data_packages = client.games_packages | ||
|  |                             local_collected_items = len(client.checked_locations) | ||
|  |                             if collected_items < 2:  # Clique only has 2 Locations | ||
|  |                                 client.collect_any() | ||
|  |                             # TODO: Ctrl+C test here as well | ||
|  | 
 | ||
|  |                     for game_name in sorted(involved_games): | ||
|  |                         expect_true(game_name in local_data_packages, | ||
|  |                                     f"{game_name} missing from MultiServer datap ackage") | ||
|  |                         expect_true("item_name_groups" not in local_data_packages.get(game_name, {}), | ||
|  |                                     f"item_name_groups are not supposed to be in MultiServer data for {game_name}") | ||
|  |                         expect_true("location_name_groups" not in local_data_packages.get(game_name, {}), | ||
|  |                                     f"location_name_groups are not supposed to be in MultiServer data for {game_name}") | ||
|  |                     for game_name in local_data_packages: | ||
|  |                         expect_true(game_name in involved_games, | ||
|  |                                     f"Received unexpected extra data package for {game_name} from MultiServer") | ||
|  |                     assert_equal(local_collected_items, collected_items, | ||
|  |                                  "MultiServer did not load or save correctly") | ||
|  | 
 | ||
|  |                     print(f"\nTesting [{n}] {game} in {multidata} on customserver with {collected_items} items collected") | ||
|  |                     prev_host_adr: str | ||
|  |                     with WebHostServeGame(webhost_client, room) as host: | ||
|  |                         prev_host_adr = host.address | ||
|  |                         with Client(host.address, game, "Player1") as client: | ||
|  |                             web_data_packages = client.games_packages | ||
|  |                             web_collected_items = len(client.checked_locations) | ||
|  |                             if collected_items < 2:  # Clique only has 2 Locations | ||
|  |                                 client.collect_any() | ||
|  |                             if collected_items == 1: | ||
|  |                                 sleep(1)  # wait for the server to collect the item | ||
|  |                                 stop_autohost(True)  # simulate Ctrl+C | ||
|  |                                 sleep(3) | ||
|  |                                 autohost(webapp.config)  # this will spin the room right up again | ||
|  |                                 sleep(1)  # make log less annoying | ||
|  |                                 # if saving failed, the next iteration will fail below | ||
|  | 
 | ||
|  |                     # verify server shut down | ||
|  |                     try: | ||
|  |                         with Client(prev_host_adr, game, "Player1") as client: | ||
|  |                             assert_true(False, "Server did not shut down") | ||
|  |                     except ConnectionError: | ||
|  |                         pass | ||
|  | 
 | ||
|  |                     for game_name in sorted(involved_games): | ||
|  |                         expect_true(game_name in web_data_packages, | ||
|  |                                     f"{game_name} missing from customserver data package") | ||
|  |                         expect_true("item_name_groups" not in web_data_packages.get(game_name, {}), | ||
|  |                                     f"item_name_groups are not supposed to be in customserver data for {game_name}") | ||
|  |                         expect_true("location_name_groups" not in web_data_packages.get(game_name, {}), | ||
|  |                                     f"location_name_groups are not supposed to be in customserver data for {game_name}") | ||
|  |                     for game_name in web_data_packages: | ||
|  |                         expect_true(game_name in involved_games, | ||
|  |                                     f"Received unexpected extra data package for {game_name} from customserver") | ||
|  |                     assert_equal(web_collected_items, collected_items, | ||
|  |                                  "customserver did not load or save correctly during/after " | ||
|  |                                  + ("Ctrl+C" if collected_items == 2 else "/exit")) | ||
|  | 
 | ||
|  |                     # compare customserver to MultiServer | ||
|  |                     expect_equal(local_data_packages, web_data_packages, | ||
|  |                                  "customserver datapackage differs from MultiServer") | ||
|  | 
 | ||
|  |             sleep(5.5)  # make sure all tasks actually stopped | ||
|  | 
 | ||
|  |             # raise an exception in customserver and verify the save doesn't get destroyed | ||
|  |             # local variables room is the last room's id here | ||
|  |             old_data = get_multidata_for_room(webhost_client, room) | ||
|  |             print(f"Destroying multidata for {room}") | ||
|  |             set_multidata_for_room(webhost_client, room, bytes([0])) | ||
|  |             try: | ||
|  |                 start_room(webhost_client, room, timeout=7) | ||
|  |             except TimeoutError: | ||
|  |                 pass | ||
|  |             else: | ||
|  |                 assert_true(False, "Room started with destroyed multidata") | ||
|  |             print(f"Restoring multidata for {room}") | ||
|  |             set_multidata_for_room(webhost_client, room, old_data) | ||
|  |             with WebHostServeGame(webhost_client, room) as host: | ||
|  |                 with Client(host.address, game, "Player1") as client: | ||
|  |                     assert_equal(len(client.checked_locations), 2, | ||
|  |                                  "Save was destroyed during exception in customserver") | ||
|  |                     print("Save file is not busted 🥳") | ||
|  | 
 | ||
|  |         finally: | ||
|  |             print("Stopping autohost") | ||
|  |             stop_autohost(False) | ||
|  | 
 | ||
|  |     if failure: | ||
|  |         print("Some tests failed") | ||
|  |         exit(1) | ||
|  |     exit(0) |