def test_decode(mock_dictionary_byte_hash: MagicMock): mock_dictionary_byte_hash.return_value = 120 # We're mocking the database hash to avoid breaking tests every single time we change the database # This test should break whenever we change how permalinks are created # When this happens, we must bump the permalink version and change the tests encoded = "gAAAfReLCAAC4wAAAOaANg==" expected = Permalink( seed_number=1000, spoiler=True, patcher_configuration=PatcherConfiguration( menu_mod=True, warp_to_start=False, ), layout_configuration=LayoutConfiguration.from_params( trick_level_configuration=TrickLevelConfiguration( LayoutTrickLevel.HARD), elevators=LayoutElevators.RANDOMIZED, ), ) # Uncomment this line to quickly get the new encoded permalink # assert expected.as_str == "" # print(expected.as_str) # Run link = Permalink.from_str(encoded) # Assert assert link == expected
def test_create_permalink_logic(mock_print: MagicMock, ): # Setup args = MagicMock() args.trick_level = LayoutTrickLevel.HARD.value args.major_items_mode = False args.sky_temple_keys = LayoutSkyTempleKeyMode.ALL_BOSSES.value args.skip_item_loss = True args.seed = 15000 args.menu_mod = False args.warp_to_start = False # Run randovania.cli.commands.create_permalink.create_permalink_logic(args) # Assert permalink = Permalink( seed_number=args.seed, spoiler=True, patcher_configuration=PatcherConfiguration( menu_mod=args.menu_mod, warp_to_start=args.warp_to_start, ), layout_configuration=LayoutConfiguration.from_params( trick_level_configuration=TrickLevelConfiguration( LayoutTrickLevel.HARD), sky_temple_keys=LayoutSkyTempleKeyMode.ALL_BOSSES, elevators=LayoutElevators.VANILLA, starting_location=StartingLocation.default(), ), ) # Assert mock_print.assert_called_once_with(permalink)
def test_distribute_command_logic(mock_generate_list: MagicMock, ): # Setup args = MagicMock() args.trick_level = LayoutTrickLevel.HARD.value args.major_items_mode = False args.sky_temple_keys = LayoutSkyTempleKeyMode.ALL_BOSSES.value args.skip_item_loss = True args.seed = 15000 args.output_file = "asdfasdf/qwerqwerqwer/zxcvzxcv.json" # Run echoes.distribute_command_logic(args) # Assert mock_generate_list.assert_called_once_with(permalink=Permalink( seed_number=args.seed, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=LayoutConfiguration.from_params( trick_level=LayoutTrickLevel.HARD, sky_temple_keys=LayoutSkyTempleKeyMode.ALL_BOSSES, elevators=LayoutRandomizedFlag.VANILLA, pickup_quantities={}, starting_location=StartingLocation.default(), starting_resources=StartingResources.from_non_custom_configuration( StartingResourcesConfiguration.VANILLA_ITEM_LOSS_DISABLED), ), ), status_update=ANY) save_file_mock: MagicMock = mock_generate_list.return_value.save_to_file save_file_mock.assert_called_once_with(Path(args.output_file))
def batch_distribute_helper( base_permalink: Permalink, seed_number: int, timeout: int, validate: bool, output_dir: Path, ) -> float: permalink = Permalink( seed_number=seed_number, spoiler=True, patcher_configuration=base_permalink.patcher_configuration, layout_configuration=base_permalink.layout_configuration, ) start_time = time.perf_counter() description = generator.generate_description( permalink=permalink, status_update=None, validate_after_generation=validate, timeout=timeout) delta_time = time.perf_counter() - start_time description.save_to_file(output_dir.joinpath( "{}.json".format(seed_number))) return delta_time
def from_json_dict(cls, json_dict: dict) -> "LayoutDescription": json_dict = migrate_description(json_dict) has_spoiler = "game_modifications" in json_dict if not has_spoiler: raise ValueError( "Unable to read details of seed log with spoiler disabled") permalink = Permalink( seed_number=json_dict["info"]["seed"], spoiler=has_spoiler, presets={ index: VersionedPreset(preset).get_preset() for index, preset in enumerate(json_dict["info"]["presets"]) }, ) return LayoutDescription( version=json_dict["info"]["version"], permalink=permalink, all_patches=game_patches_serializer.decode( json_dict["game_modifications"], { index: preset.configuration for index, preset in permalink.presets.items() }), item_order=json_dict["item_order"], )
def from_json_dict(cls, json_dict: dict) -> "LayoutDescription": version = json_dict.get("schema_version") if version != cls.schema_version(): raise RuntimeError("Unsupported log file version '{}'. Expected {}.".format(version, cls.schema_version())) has_spoiler = "game_modifications" in json_dict if not has_spoiler: raise ValueError("Unable to read details of seed log with spoiler disabled") permalink = Permalink( seed_number=json_dict["info"]["seed"], spoiler=has_spoiler, presets={ index: VersionedPreset(preset).get_preset() for index, preset in enumerate(json_dict["info"]["presets"]) }, ) return LayoutDescription( version=json_dict["info"]["version"], permalink=permalink, all_patches=game_patches_serializer.decode( json_dict["game_modifications"], { index: preset.layout_configuration for index, preset in permalink.presets.items() }), item_order=json_dict["item_order"], )
def batch_distribute_helper( base_permalink, seed_number: int, timeout: int, validate: bool, output_dir: Path, ) -> float: from randovania.generator import generator from randovania.layout.permalink import Permalink permalink = Permalink( seed_number=seed_number, spoiler=True, presets=typing.cast(Permalink, base_permalink).presets, ) start_time = time.perf_counter() description = asyncio.run( generator.generate_and_validate_description( permalink=permalink, status_update=None, validate_after_generation=validate, timeout=timeout, attempts=0, )) delta_time = time.perf_counter() - start_time description.save_to_file( output_dir.joinpath("{}.{}".format(seed_number, description.file_extension()))) return delta_time
def _create_test_layout_description( configuration: LayoutConfiguration, pickup_mapping: Iterable[int], ) -> LayoutDescription: """ Creates a LayoutDescription for the given configuration, with the patches being for the given pickup_mapping :param configuration: :param pickup_mapping: :return: """ game = data_reader.decode_data(configuration.game_data) pickup_database = game.pickup_database return LayoutDescription( version=VERSION, permalink=Permalink( seed_number=0, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=configuration, ), patches=GamePatches.with_game(game).assign_new_pickups([ (PickupIndex(i), pickup_database.original_pickup_mapping[PickupIndex(new_index)]) for i, new_index in enumerate(pickup_mapping) ]), solver_path=())
def test_round_trip_generated_patches(echoes_game_data, preset_manager): # Setup preset = dataclasses.replace( preset_manager.default_preset, layout_configuration=dataclasses.replace( preset_manager.default_preset.layout_configuration, trick_level_configuration=TrickLevelConfiguration( global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS, specific_levels={}, ))) all_patches = generator._async_create_description( permalink=Permalink( seed_number=1000, spoiler=True, presets={0: preset}, ), status_update=lambda x: None, ).all_patches # Run encoded = game_patches_serializer.serialize(all_patches, {0: echoes_game_data}) decoded = game_patches_serializer.decode(encoded, {0: preset.layout_configuration}) # Assert assert all_patches == decoded
def test_decode_mock_other(encoded, num_players, mocker): preset = MagicMock() def read_values(decoder: BitPackDecoder, metadata): decoder.decode(100, 100) return preset mock_preset_unpack: MagicMock = mocker.patch( "randovania.layout.preset.Preset.bit_pack_unpack", side_effect=read_values) expected = Permalink( seed_number=1000, spoiler=True, presets={i: preset for i in range(num_players)}, ) preset.bit_pack_encode.return_value = [(0, 100), (5, 100)] # Uncomment this line to quickly get the new encoded permalink # assert expected.as_base64_str == "" # print(expected.as_base64_str) # Run round_trip = expected.as_base64_str link = Permalink.from_str(encoded) # Assert assert link == expected assert round_trip == encoded mock_preset_unpack.assert_called_once_with(ANY, {"manager": ANY})
def distribute_command_logic(args): debug._DEBUG_LEVEL = args.debug def status_update(s): pass seed_number = args.seed if seed_number is None: seed_number = random.randint(0, 2**31) print("Using seed: {}".format(seed_number)) permalink = Permalink( seed_number=seed_number, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=get_layout_configuration_from_args(args), ) before = time.perf_counter() layout_description = generator.generate_list(permalink=permalink, status_update=status_update) after = time.perf_counter() print("Took {} seconds. Hash: {}".format( after - before, hash(tuple(layout_description.pickup_assignment.items())))) layout_description.save_to_file(Path(args.output_file))
def test_round_trip(spoiler: bool, layout: dict, default_preset, mocker): # Setup random_uuid = uuid.uuid4() mocker.patch("uuid.uuid4", return_value=random_uuid) preset = Preset( name="{} Custom".format(default_preset.name), description="A customized preset.", uuid=random_uuid, base_preset_uuid=default_preset.uuid, game=default_preset.game, configuration=dataclasses.replace(default_preset.configuration, **layout), ) link = Permalink( seed_number=1000, spoiler=spoiler, presets={0: preset}, ) # Run after = Permalink.from_str(link.as_base64_str) # Assert assert link == after
def test_round_trip_generated_patches(echoes_game_data, default_preset): # Setup preset = dataclasses.replace( default_preset, base_preset_name=default_preset.name, configuration=dataclasses.replace( default_preset.configuration, trick_level=TrickLevelConfiguration( minimal_logic=True, specific_levels={}, game=RandovaniaGame.PRIME2, ) ) ) all_patches = generator._async_create_description( permalink=Permalink( seed_number=1000, spoiler=True, presets={0: preset}, ), status_update=lambda x: None, attempts=0, ).all_patches # Run encoded = game_patches_serializer.serialize(all_patches, {0: echoes_game_data}) decoded = game_patches_serializer.decode(encoded, {0: preset.configuration}) # Assert assert all_patches == decoded
def test_decode_mock_other( mock_packer_unpack: MagicMock, mock_layout_unpack: MagicMock, ): encoded = "gAAAfRggLQ==" patcher_configuration = mock_packer_unpack.return_value layout_configuration = mock_layout_unpack.return_value expected = Permalink( seed_number=1000, spoiler=True, patcher_configuration=patcher_configuration, layout_configuration=layout_configuration, ) patcher_configuration.bit_pack_encode.return_value = [] layout_configuration.bit_pack_encode.return_value = [] mock_layout_unpack.return_value.game_data = {"test": True} # Uncomment this line to quickly get the new encoded permalink # assert expected.as_str == "" # print(expected.as_str) # Run link = Permalink.from_str(encoded) round_trip = expected.as_str # Assert assert link == expected assert encoded == round_trip mock_packer_unpack.assert_called_once() mock_layout_unpack.assert_called_once() patcher_configuration.bit_pack_encode.assert_called_once_with({}) layout_configuration.bit_pack_encode.assert_called_once_with({})
def test_batch_distribute_helper( mock_perf_counter: MagicMock, mock_generate_description: MagicMock, ): # Setup base_permalink = MagicMock() seed_number = 5000 validate = MagicMock() output_dir = MagicMock() expected_permalink = Permalink( seed_number=seed_number, spoiler=True, patcher_configuration=base_permalink.patcher_configuration, layout_configuration=base_permalink.layout_configuration, ) mock_perf_counter.side_effect = [1000, 5000] # Run delta_time = batch_distribute.batch_distribute_helper( base_permalink, seed_number, validate, output_dir) # Assert mock_generate_description.assert_called_once_with(expected_permalink, None, validate) assert delta_time == 4000 output_dir.joinpath.assert_called_once_with("{}.json".format(seed_number)) mock_generate_description.return_value.save_to_file.assert_called_once_with( output_dir.joinpath.return_value)
async def test_generate_game(window, mocker, preset_manager): mock_generate_layout: MagicMock = mocker.patch("randovania.interface_common.simplified_patcher.generate_layout") mock_randint: MagicMock = mocker.patch("random.randint", return_value=5000) spoiler = True game_session = MagicMock() game_session.presets = [preset_manager.default_preset, preset_manager.default_preset] window._game_session = game_session window._upload_layout_description = AsyncMock() window._admin_global_action = AsyncMock() # Run await window.generate_game(spoiler) # Assert mock_randint.assert_called_once_with(0, 2 ** 31) mock_generate_layout.assert_called_once_with( progress_update=ANY, permalink=Permalink( seed_number=mock_randint.return_value, spoiler=spoiler, presets={ 0: preset_manager.default_preset.get_preset(), 1: preset_manager.default_preset.get_preset(), }, ), options=window._options ) window._upload_layout_description.assert_awaited_once_with(mock_generate_layout.return_value)
def test_round_trip_generated_patches(echoes_game_data): # Setup configuration = LayoutConfiguration.from_params( trick_level_configuration=TrickLevelConfiguration( global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS, specific_levels={}, )) patches = generator._create_randomized_patches( permalink=Permalink( seed_number=1000, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=configuration, ), game=data_reader.decode_data(echoes_game_data), status_update=lambda x: None, ) # Run encoded = game_patches_serializer.serialize(patches, echoes_game_data) decoded = game_patches_serializer.decode(encoded, configuration) # Assert assert patches == decoded
def _generate_new_seed(self, spoiler: bool): self.generate_seed_from_permalink( Permalink( seed_number=random.randint(0, 2**31), spoiler=spoiler, preset=self._current_preset_data, ))
async def test_round_trip_generated_patches(default_preset): # Setup preset = dataclasses.replace( default_preset, uuid=uuid.UUID('b41fde84-1f57-4b79-8cd6-3e5a78077fa6'), base_preset_uuid=default_preset.uuid, configuration=dataclasses.replace(default_preset.configuration, trick_level=TrickLevelConfiguration( minimal_logic=True, specific_levels={}, game=default_preset.game, ))) description = await generator._create_description( permalink=Permalink( seed_number=1000, spoiler=True, presets={0: preset}, ), status_update=lambda x: None, attempts=0, ) all_patches = description.all_patches # Run encoded = game_patches_serializer.serialize(all_patches, {0: default_preset.game}) decoded = game_patches_serializer.decode(encoded, {0: preset.configuration}) # Assert assert all_patches == decoded
def test_batch_distribute_helper( mock_perf_counter: MagicMock, mock_generate_description: MagicMock, ): # Setup base_permalink = MagicMock() seed_number = 5000 validate = MagicMock() output_dir = MagicMock() timeout = 67 expected_permalink = Permalink( seed_number=seed_number, spoiler=True, presets=base_permalink.presets, ) mock_perf_counter.side_effect = [1000, 5000] # Run delta_time = batch_distribute.batch_distribute_helper( base_permalink, seed_number, timeout, validate, output_dir) # Assert mock_generate_description.assert_called_once_with( permalink=expected_permalink, status_update=None, validate_after_generation=validate, timeout=timeout) assert delta_time == 4000 output_dir.joinpath.assert_called_once_with("{}.json".format(seed_number)) mock_generate_description.return_value.save_to_file.assert_called_once_with( output_dir.joinpath.return_value)
def test_round_trip_generated_patches(echoes_game_data, preset_manager): # Setup preset = dataclasses.replace( preset_manager.default_preset, layout_configuration=dataclasses.replace( preset_manager.default_preset.layout_configuration, trick_level_configuration=TrickLevelConfiguration( global_level=LayoutTrickLevel.MINIMAL_RESTRICTIONS, specific_levels={}, ) ) ) patches = generator._create_randomized_patches( permalink=Permalink( seed_number=1000, spoiler=True, preset=preset, ), game=data_reader.decode_data(echoes_game_data), status_update=lambda x: None, ) # Run encoded = game_patches_serializer.serialize(patches, echoes_game_data) decoded = game_patches_serializer.decode(encoded, preset.layout_configuration) # Assert assert patches == decoded
def test_apply_layout( mock_ensure_no_menu_mod: MagicMock, mock_create_pak_backups: MagicMock, mock_add_menu_mod_to_files: MagicMock, mock_modern_api: MagicMock, mock_create_progress_update_from_successive_messages: MagicMock, mock_save_to_file: MagicMock, include_menu_mod: bool, has_backup_path: bool, ): # Setup cosmetic_patches = MagicMock() description = LayoutDescription( version=randovania.VERSION, permalink=Permalink(seed_number=1, spoiler=False, patcher_configuration=PatcherConfiguration( menu_mod=include_menu_mod, warp_to_start=MagicMock(), ), layout_configuration=MagicMock()), patches=MagicMock(), solver_path=(), ) game_root = MagicMock(spec=Path()) backup_files_path = MagicMock() if has_backup_path else None progress_update = MagicMock() status_update = mock_create_progress_update_from_successive_messages.return_value # Run claris_randomizer.apply_layout(description, cosmetic_patches, backup_files_path, progress_update, game_root) # Assert mock_create_progress_update_from_successive_messages.assert_called_once_with( progress_update, 400 if include_menu_mod else 100) mock_ensure_no_menu_mod.assert_called_once_with(game_root, backup_files_path, status_update) if has_backup_path: mock_create_pak_backups.assert_called_once_with( game_root, backup_files_path, status_update) else: mock_create_pak_backups.assert_not_called() game_root.joinpath.assert_called_once_with("files", "randovania.json") mock_save_to_file.assert_called_once_with(description, game_root.joinpath.return_value) mock_modern_api.assert_called_once_with(game_root, status_update, description, cosmetic_patches) if include_menu_mod: mock_add_menu_mod_to_files.assert_called_once_with( game_root, status_update) else: mock_add_menu_mod_to_files.assert_not_called()
def permalink(self) -> Optional[Permalink]: if self.seed_number is None: return None return Permalink( seed_number=self.seed_number, spoiler=self.create_spoiler, patcher_configuration=self.patcher_configuration, layout_configuration=self.layout_configuration, )
async def _create_permalink(args_) -> Permalink: from randovania.interface_common.preset_manager import PresetManager preset_manager = PresetManager(None) preset = preset_manager.included_preset_with(RandovaniaGame(args_.game), args_.preset_name).get_preset() return Permalink( args_.seed_number, spoiler=True, presets={i: preset for i in range(args_.player_count)}, )
def test_encode(fake_generator_parameters): # Setup link = Permalink(parameters=fake_generator_parameters, seed_hash=None, randovania_version=b"0123") # Run encoded = link.as_base64_str # Assert assert encoded == "DX4wMTIzAx6sOvRw"
def _generate_new_seed(self, spoiler: bool): preset = self._current_preset_data num_players = self.window.num_players_spin_box.value() self.generate_seed_from_permalink( Permalink( seed_number=random.randint(0, 2**31), spoiler=spoiler, presets={i: preset.get_preset() for i in range(num_players)}, ))
async def _create_permalink(args) -> Permalink: from randovania.interface_common import persistence preset_manager = PresetManager(persistence.user_data_dir()) await preset_manager.load_user_presets() preset = preset_manager.preset_for_name(args.preset_name).get_preset() return Permalink( args.seed_number, spoiler=True, presets={i: preset for i in range(args.player_count)}, )
def run_bootstrap(preset: Preset): game = data_reader.decode_data(preset.configuration.game_data) permalink = Permalink( seed_number=15000, spoiler=True, presets={0: preset}, ) patches = base_patches_factory.create_base_patches(preset.configuration, Random(15000), game, False) _, state = logic_bootstrap(preset.configuration, game, patches) return game, state, permalink
def test_create_patches( mock_random: MagicMock, mock_calculate_item_pool: MagicMock, mock_create_base_patches: MagicMock, mock_retcon_playthrough_filler: MagicMock, mock_indices_for_unassigned_pickups: MagicMock, ): # Setup seed_number: int = 91319 game = default_prime2_game_description() status_update: Union[MagicMock, Callable[[str], None]] = MagicMock() configuration = LayoutConfiguration.from_params( trick_level=LayoutTrickLevel.NO_TRICKS, sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM, elevators=LayoutRandomizedFlag.VANILLA, pickup_quantities={}, starting_location=StartingLocation.default(), starting_resources=StartingResources.from_item_loss(False), ) permalink = Permalink( seed_number=seed_number, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=configuration, ) mock_calculate_item_pool.return_value = list( sorted(game.pickup_database.original_pickup_mapping.values())) mock_create_base_patches.return_value.starting_location = game.starting_location mock_create_base_patches.return_value.custom_initial_items = None filler_patches = mock_retcon_playthrough_filler.return_value # Run result = generator._create_patches(permalink, game, status_update) # Assert mock_random.assert_called_once_with(permalink.as_str) mock_calculate_item_pool.assert_called_once_with(permalink, game) mock_create_base_patches.assert_called_once_with(mock_random.return_value, game, permalink, ANY) mock_retcon_playthrough_filler.assert_called_once_with( ANY, ANY, ANY, mock_random.return_value, status_update) mock_indices_for_unassigned_pickups.assert_called_once_with( mock_random.return_value, game, filler_patches.pickup_assignment, ANY) filler_patches.assign_new_pickups.assert_called_once_with( mock_indices_for_unassigned_pickups.return_value) assert result == filler_patches.assign_new_pickups.return_value
def test_round_trip(spoiler: bool, patcher: PatcherConfiguration, layout: LayoutConfiguration): # Setup link = Permalink( seed_number=1000, spoiler=spoiler, patcher_configuration=patcher, layout_configuration=layout, ) # Run after = Permalink.from_str(link.as_str) # Assert assert link == after