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_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_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 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 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
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, }, starting_location=StartingLocation.default(), starting_resources=StartingResources.default(), ),
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]
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()
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()
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(), ), ])