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_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 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 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 default(cls) -> "Permalink": return Permalink( seed_number=0, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=LayoutConfiguration.default(), )
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 _decode_preset(decoder: BitPackDecoder, manager: PresetManager) -> Preset: included_presets = [versioned.get_preset() for versioned in manager.included_presets] is_custom_preset = bitpacking.decode_bool(decoder) reference_preset = decoder.decode_element(included_presets) if is_custom_preset: patcher_configuration = PatcherConfiguration.bit_pack_unpack( decoder, {"reference": reference_preset.patcher_configuration}) layout_configuration = LayoutConfiguration.bit_pack_unpack( decoder, {"reference": reference_preset.layout_configuration}) preset = Preset( name="{} Custom".format(reference_preset.name), description="A customized preset.", base_preset_name=reference_preset.name, patcher_configuration=patcher_configuration, layout_configuration=layout_configuration, ) else: preset = reference_preset included_data_hash = decoder.decode_single(256) expected_data_hash = _dictionary_byte_hash(preset.layout_configuration.game_data) if included_data_hash != expected_data_hash: raise ValueError("Given permalink is for a Randovania database with hash '{}', " "but current database has hash '{}'.".format(included_data_hash, expected_data_hash)) return preset
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_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 from_json_dict(cls, param: dict) -> "Permalink": return Permalink( seed_number=param["seed"], spoiler=param["spoiler"], patcher_configuration=PatcherConfiguration.from_json_dict( param["patcher_configuration"]), layout_configuration=LayoutConfiguration.from_json_dict( param["layout_configuration"]), )
def test_decode(patcher_with_data): # Setup data, expected = patcher_with_data # Run decoder = BitPackDecoder(data) result = PatcherConfiguration.bit_pack_unpack(decoder, {}) # Assert assert result == expected
def from_json_dict(cls, value) -> "Preset": return Preset( name=value["name"], description=value["description"], base_preset_name=value["base_preset_name"], patcher_configuration=PatcherConfiguration.from_json_dict( value["patcher_configuration"]), layout_configuration=LayoutConfiguration.from_json_dict( value["layout_configuration"]), )
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 create_permalink_logic(args): seed_number = args.seed if seed_number is None: seed_number = random.randint(0, 2 ** 31) permalink = Permalink( seed_number=seed_number, spoiler=True, patcher_configuration=PatcherConfiguration( menu_mod=args.menu_mod, warp_to_start=args.warp_to_start, ), layout_configuration=get_layout_configuration_from_args(args), ) _print_permalink(permalink)
def batch_distribute_helper(args, seed_number) -> float: data = prime_database.decode_data_file(args) configuration = get_layout_configuration_from_args(args) permalink = Permalink( seed_number=seed_number, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=configuration, ) start_time = time.perf_counter() description = generator.generate_list(permalink, None) delta_time = time.perf_counter() - start_time description.save_to_file( Path(args.output_dir, "{}.json".format(seed_number))) return delta_time
def _test_data(): data = default_data.decode_default_prime2() game = data_reader.decode_data(data) configuration = LayoutConfiguration.from_params() permalink = Permalink( seed_number=15000, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=configuration, ) patches = GamePatches.with_game(game) patches = patches.assign_gate_assignment( base_patches_factory.gate_assignment_for_configuration( configuration, game.resource_database, Random(15000))) game, state = logic_bootstrap(configuration, game, patches) return game, state, permalink
def test_encode(mock_dictionary_byte_hash: MagicMock): # Setup mock_dictionary_byte_hash.return_value = 120 link = Permalink( seed_number=1000, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=LayoutConfiguration.default(), ) # Run encoded = link.as_str # Assert mock_dictionary_byte_hash.assert_called_once_with( link.layout_configuration.game_data) assert encoded == "MAAAfReMYADv"
def test_output_name_for(mock_shareable_hash: PropertyMock, empty_patches): # Setup permalink_mock = MagicMock(spec=Permalink( seed_number=15000, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=LayoutConfiguration.default(), )) layout = LayoutDescription(version="0.15.0", permalink=permalink_mock, patches=empty_patches, solver_path=()) mock_shareable_hash.return_value = "PermalinkStr" # Run result = simplified_patcher._output_name_for(layout) # Assert assert result == "Echoes Randomizer - PermalinkStr"
def bit_pack_unpack(cls, decoder: BitPackDecoder) -> "Permalink": version, seed, spoiler = decoder.decode(_PERMALINK_MAX_VERSION, _PERMALINK_MAX_SEED, 2) cls._raise_if_different_version(version) included_data_hash = decoder.decode(256)[0] patcher_configuration = PatcherConfiguration.bit_pack_unpack(decoder) layout_configuration = LayoutConfiguration.bit_pack_unpack(decoder) expected_data_hash = _dictionary_byte_hash( layout_configuration.game_data) if included_data_hash != expected_data_hash: raise ValueError( "Given permalink is for a Randovania database with hash '{}', " "but current database has hash '{}'.".format( included_data_hash, expected_data_hash)) return Permalink(seed, bool(spoiler), patcher_configuration, layout_configuration)
def _test_data(): data = default_data.decode_default_prime2() game = data_reader.decode_data(data, False) 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.default(), ) permalink = Permalink( seed_number=15000, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=configuration, ) logic, state = logic_bootstrap(configuration, game, GamePatches.with_game(game)) return logic, state, permalink
def bit_pack_unpack(cls, decoder: BitPackDecoder, metadata) -> "Permalink": version, seed, spoiler = decoder.decode(_PERMALINK_MAX_VERSION, _PERMALINK_MAX_SEED, 2) cls._raise_if_different_version(version) included_data_hash = decoder.decode_single(256) manager = PresetManager(None) is_custom_preset = bitpacking.decode_bool(decoder) reference_preset = decoder.decode_element(manager.included_presets) if is_custom_preset: patcher_configuration = PatcherConfiguration.bit_pack_unpack( decoder, {"reference": reference_preset.patcher_configuration}) layout_configuration = LayoutConfiguration.bit_pack_unpack( decoder, {"reference": reference_preset.layout_configuration}) preset = Preset( name="{} Custom".format(reference_preset.name), description="A customized preset.", base_preset_name=reference_preset.name, patcher_configuration=patcher_configuration, layout_configuration=layout_configuration, ) else: preset = reference_preset expected_data_hash = _dictionary_byte_hash( preset.layout_configuration.game_data) if included_data_hash != expected_data_hash: raise ValueError( "Given permalink is for a Randovania database with hash '{}', " "but current database has hash '{}'.".format( included_data_hash, expected_data_hash)) return Permalink( seed, bool(spoiler), preset, )
def test_generate_seed_with_invalid_quantity_configuration(): # Setup status_update = MagicMock() configuration = LayoutConfiguration.from_params( trick_level=LayoutTrickLevel.NO_TRICKS, sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM, elevators=LayoutRandomizedFlag.VANILLA, pickup_quantities={"Light Suit": 5}, starting_location=StartingLocation.default(), starting_resources=StartingResources.default(), ) permalink = Permalink( seed_number=50, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=configuration, ) # Run with pytest.raises(randovania.resolver.exceptions.GenerationFailure): generator.generate_list(permalink, status_update=status_update)
def test_decode_v1(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 = "IAAAfReNPArRHMClxLYgIID3" expected = Permalink( seed_number=1000, spoiler=True, patcher_configuration=PatcherConfiguration( disable_hud_popup=True, menu_mod=True, speed_up_credits=False, ), layout_configuration=LayoutConfiguration.from_params( trick_level=LayoutTrickLevel.HARD, sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM, elevators=LayoutRandomizedFlag.RANDOMIZED, pickup_quantities={ "Missile Expansion": 10, "Light Suit": 9, }, starting_location=StartingLocation.default(), starting_resources=StartingResources.default(), ), ) # Uncomment this line to quickly get the new encoded permalink # assert expected.as_str == "" # Run link = Permalink.from_str(encoded) # Assert assert link == expected
def test_apply_layout(mock_run_with_args: MagicMock, mock_base_args: MagicMock, mock_ensure_no_menu_mod: MagicMock, mock_create_pak_backups: MagicMock, mock_calculate_indices: MagicMock, mock_add_menu_mod_to_files: MagicMock, mock_create_progress_update_from_successive_messages: MagicMock, mock_save_to_file: MagicMock, seed_number: int, item_loss: bool, elevators: bool, warp_to_start: bool, include_menu_mod: bool, speed_up_credits: bool, ): # Setup hud_memo_popup_removal: bool = MagicMock() cosmetic_patches = CosmeticPatches(disable_hud_popup=hud_memo_popup_removal, speed_up_credits=speed_up_credits, ) description = LayoutDescription( version=randovania.VERSION, permalink=Permalink( seed_number=seed_number, spoiler=False, patcher_configuration=PatcherConfiguration( menu_mod=include_menu_mod, warp_to_start=warp_to_start, ), layout_configuration=LayoutConfiguration.from_params( trick_level=MagicMock(), sky_temple_keys=MagicMock(), elevators=LayoutRandomizedFlag.RANDOMIZED if elevators else LayoutRandomizedFlag.VANILLA, pickup_quantities={}, starting_location=StartingLocation.default(), starting_resources=StartingResources.from_item_loss(item_loss), ) ), patches=None, solver_path=(), ) game_root = MagicMock(spec=Path()) backup_files_path = MagicMock() progress_update = MagicMock() status_update = mock_create_progress_update_from_successive_messages.return_value mock_calculate_indices.return_value = [10, 25, 1, 2, 5, 1] mock_base_args.return_value = [] expected_args = [ "-s", str(seed_number), "-p", "10,25,1,2,5,1" ] if not item_loss: expected_args.append("-i") if elevators: expected_args.append("-v") if speed_up_credits: expected_args.append("-c") if warp_to_start: expected_args.append("-t") # Run claris_randomizer.apply_layout(description, cosmetic_patches, backup_files_path, progress_update, game_root) # Assert mock_base_args.assert_called_once_with(game_root, hud_memo_popup_removal=hud_memo_popup_removal) 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) mock_create_pak_backups.assert_called_once_with(game_root, backup_files_path, status_update) mock_calculate_indices.assert_called_once_with(description) 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_run_with_args.assert_called_once_with(expected_args, "Randomized!", status_update) 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()
from randovania.layout.layout_configuration import LayoutTrickLevel, LayoutConfiguration from randovania.layout.layout_description import LayoutDescription, SolverPath, _item_locations_to_pickup_assignment from randovania.layout.patcher_configuration import PatcherConfiguration from randovania.layout.permalink import Permalink @pytest.mark.parametrize("value", LayoutTrickLevel) def test_pickle_trick_level(value: LayoutTrickLevel): assert pickle.loads(pickle.dumps(value)) == value @pytest.mark.parametrize("permalink", [ Permalink( seed_number=1000, spoiler=True, patcher_configuration=PatcherConfiguration.default(), layout_configuration=LayoutConfiguration.default(), ) ]) @pytest.mark.parametrize("item_locations", [{}]) @pytest.mark.parametrize("solver_path", [tuple()]) def test_round_trip_default(permalink: Permalink, item_locations: Dict[str, Dict[str, str]], solver_path: Tuple[SolverPath, ...]): game = data_reader.decode_data(permalink.layout_configuration.game_data) original = LayoutDescription( version=randovania.VERSION, permalink=permalink, patches=GamePatches( _item_locations_to_pickup_assignment(game, item_locations),
mock_dictionary_byte_hash.assert_called_once_with( link.layout_configuration.game_data) assert encoded == "MAAAfReMYADv" @pytest.mark.parametrize( "invalid", ["", "a", "x", "zz", "AAAAfRxALWmCI50gIQD", "AAAAfRxALxmCI50gIQDy"]) def test_decode_invalid(invalid: str): with pytest.raises(ValueError): Permalink.from_str(invalid) @pytest.mark.parametrize("spoiler", [False, True]) @pytest.mark.parametrize("patcher", [ PatcherConfiguration.default(), PatcherConfiguration( menu_mod=True, warp_to_start=False, ), ]) @pytest.mark.parametrize("layout", [ LayoutConfiguration.default(), LayoutConfiguration.from_params( trick_level=LayoutTrickLevel.HARD, sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM, elevators=LayoutRandomizedFlag.RANDOMIZED, pickup_quantities={ "Missile Expansion": 10, "Light Suit": 9, },
def describe(preset: Preset) -> Iterable[PresetDescription]: patcher = preset.patcher_configuration configuration = preset.layout_configuration major_items = configuration.major_items_configuration game_description = data_reader.decode_data( preset.layout_configuration.game_data) format_params = {} def _bool_to_str(b: bool) -> str: if b: return "Yes" else: return "No" # Item Placement random_starting_items = "{} to {}".format( major_items.minimum_random_starting_items, major_items.maximum_random_starting_items, ) if random_starting_items == "0 to 0": random_starting_items = "None" format_params[ "trick_level"] = configuration.trick_level_configuration.pretty_description format_params[ "randomization_mode"] = configuration.randomization_mode.value format_params["random_starting_items"] = random_starting_items # Items format_params["progressive_suit"] = _bool_to_str( major_items.progressive_suit) format_params["progressive_grapple"] = _bool_to_str( major_items.progressive_grapple) format_params["split_beam_ammo"] = _bool_to_str( configuration.split_beam_ammo) format_params["starting_items"] = _calculate_starting_items( configuration.major_items_configuration.items_state) format_params["item_pool"] = _calculate_item_pool( configuration.major_items_configuration) # Difficulty default_patcher = PatcherConfiguration() if patcher.varia_suit_damage == default_patcher.varia_suit_damage and ( patcher.dark_suit_damage == default_patcher.dark_suit_damage): dark_aether_suit_damage = "Normal" else: dark_aether_suit_damage = "Custom" format_params["dark_aether_suit_damage"] = dark_aether_suit_damage format_params[ "dark_aether_damage_strictness"] = configuration.damage_strictness.long_name format_params["pickup_model"] = patcher.pickup_model_style.value # Gameplay translator_gates = "Custom" translator_configurations = [ (configuration.translator_configuration.with_vanilla_actual(), "Vanilla (Actual)"), (configuration.translator_configuration.with_vanilla_colors(), "Vanilla (Colors)"), (configuration.translator_configuration.with_full_random(), "Random"), ] for translator_config, name in translator_configurations: if translator_config == configuration.translator_configuration: translator_gates = name break starting_locations = configuration.starting_location.locations if len(starting_locations) == 1: area = game_description.world_list.area_by_area_location( next(iter(starting_locations))) format_params[ "starting_location"] = game_description.world_list.area_name( area, distinguish_dark_aether=True) else: format_params["starting_location"] = "{} locations".format( len(starting_locations)) format_params["translator_gates"] = translator_gates format_params["elevators"] = configuration.elevators.value format_params["hints"] = "Yes" # Game Changes missile_launcher_required = True main_pb_required = True for ammo, state in configuration.ammo_configuration.items_state.items(): if ammo.name == "Missile Expansion": missile_launcher_required = state.requires_major_item elif ammo.name == "Power Bomb Expansion": main_pb_required = state.requires_major_item format_params["missile_launcher_required"] = _bool_to_str( missile_launcher_required) format_params["main_pb_required"] = _bool_to_str(main_pb_required) format_params["warp_to_start"] = _bool_to_str(patcher.warp_to_start) format_params["generic_patches"] = "Some" format_params["menu_mod"] = _bool_to_str(patcher.menu_mod) format_params["include_final_bosses"] = _bool_to_str( not configuration.skip_final_bosses) # Sky Temple Keys if configuration.sky_temple_keys.num_keys == LayoutSkyTempleKeyMode.ALL_BOSSES: stk_location = "Bosses" elif configuration.sky_temple_keys.num_keys == LayoutSkyTempleKeyMode.ALL_GUARDIANS: stk_location = "Guardians" else: stk_location = "Random" format_params["target"] = "{0} of {0}".format( configuration.sky_temple_keys.num_keys) format_params["location"] = stk_location for category, templates in _TEMPLATE_STRINGS.items(): yield category, [item.format(**format_params) for item in templates]
mock_dictionary_byte_hash.assert_called_once_with( link.layout_configuration.game_data) assert encoded == "IAAAfReOMACm" @pytest.mark.parametrize( "invalid", ["", "a", "x", "zz", "AAAAfRxALWmCI50gIQD", "AAAAfRxALxmCI50gIQDy"]) def test_decode_invalid(invalid: str): with pytest.raises(ValueError): Permalink.from_str(invalid) @pytest.mark.parametrize("spoiler", [False, True]) @pytest.mark.parametrize("patcher", [ PatcherConfiguration.default(), PatcherConfiguration( disable_hud_popup=False, menu_mod=True, speed_up_credits=False), ]) @pytest.mark.parametrize("layout", [ LayoutConfiguration.default(), LayoutConfiguration.from_params( trick_level=LayoutTrickLevel.HARD, sky_temple_keys=LayoutSkyTempleKeyMode.FULLY_RANDOM, elevators=LayoutRandomizedFlag.RANDOMIZED, pickup_quantities={ "Missile Expansion": 10, "Light Suit": 9, }, starting_location=StartingLocation.default(), starting_resources=StartingResources.default(),
def _patcher_with_data(request): return request.param["encoded"], PatcherConfiguration.from_json_dict( request.param["json"])
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_apply_patches: 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, presets={ 0: Preset(name="Name", description="Desc", base_preset_name=None, patcher_configuration=PatcherConfiguration( menu_mod=include_menu_mod, warp_to_start=MagicMock(), ), layout_configuration=MagicMock()) }, ), all_patches={0: MagicMock()}, item_order=(), ) game_root = MagicMock(spec=Path()) backup_files_path = MagicMock() if has_backup_path else None progress_update = MagicMock() player_config = PlayersConfiguration(0, {0: "you"}) status_update = mock_create_progress_update_from_successive_messages.return_value # Run claris_randomizer.apply_layout(description, player_config, 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.{}".format(LayoutDescription.file_extension())) 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, player_config, cosmetic_patches) mock_apply_patches.assert_called_once_with(game_root, description.all_patches[0], 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()