logo SERENITY
Game Systems Foundation
Search Results for

    Show / Hide Table of Contents

    Class PrefetchPolicyCreatorTests

    EditMode tests for PrefetchPolicyCreator. Every test targets fixture asset paths under a private temp folder — the real project's own PrefetchPolicyRegistry/PrefetchPolicyProfile assets, if any, are never touched — and every test passes an explicit RegistryAssetPath so the project-wide registry search never resolves to (or is made ambiguous by) the real project's own registry asset.

    Inheritance
    object
    PrefetchPolicyCreatorTests
    Inherited Members
    object.ToString()
    object.Equals(object)
    object.Equals(object, object)
    object.ReferenceEquals(object, object)
    object.GetHashCode()
    object.GetType()
    object.MemberwiseClone()
    Namespace: Serenity.AssetPrefetcher.Tests.Editor.Builder
    Assembly: Serenity.AssetPrefetcher.Tests.Editor.dll
    Syntax
    public class PrefetchPolicyCreatorTests

    Constructors

    PrefetchPolicyCreatorTests()

    Declaration
    public PrefetchPolicyCreatorTests()

    Methods

    ApplyProfileRowToDraft_EveryAssetFieldHasALikeNamedRowFieldOrIsExplicitlyExcluded()

    Declaration
    public void ApplyProfileRowToDraft_EveryAssetFieldHasALikeNamedRowFieldOrIsExplicitlyExcluded()

    CreateFromDrafts_CreateOnlyModeWithUnboundRowMatchingAnExistingProfile_RefusesAndWritesNothing()

    The write-boundary half of the CreatePrefetchPolicyWindow cache-staleness guarantee: with CreateOnly, an unbound row (no Serenity.Global.Infrastructure.Editor.SerenityWizardScratchDraft`1.SourceAsset) whose Id resolves to ANY existing in-folder profile is refused before the transaction opens — not only one owned by another row in the same batch, which ValidateNoUnboundRowClaimsAnotherRowsSource alone would miss. Exercised directly against CreateFromDrafts(IReadOnlyList<SerenityWizardScratchDraft<PrefetchPolicyProfile>>, string, string, bool, UnboundRowWriteMode) so this holds independent of any caller-side row-binding cache.

    Declaration
    public void CreateFromDrafts_CreateOnlyModeWithUnboundRowMatchingAnExistingProfile_RefusesAndWritesNothing()

    CreateFromDrafts_UnboundRowResolvesOntoAnotherRowsOwnSource_RefusesAndWritesNothing()

    A bound row can never touch another asset by construction (it resolves to its own SourceAsset, never by Id), but this proves the creator does not simply TRUST that an UNBOUND row's own Id-in-folder resolution stays clear of what another row in the same batch already owns — even if a caller-side validation gap let a colliding batch through, CreateFromDrafts itself refuses before its transaction ever opens, and nothing is written.

    Declaration
    public void CreateFromDrafts_UnboundRowResolvesOntoAnotherRowsOwnSource_RefusesAndWritesNothing()

    CreateFromDrafts_UpdateExistingByIdModeWithUnboundRowMatchingAnExistingProfile_UpdatesInPlace()

    Sanity check for UpdateExistingById (the mode Create(Input) passes): an unbound row still resolves onto an existing same-Id asset and updates it in place — the documented headless behavior, unaffected by the window's own CreateOnly refusal.

    Declaration
    public void CreateFromDrafts_UpdateExistingByIdModeWithUnboundRowMatchingAnExistingProfile_UpdatesInPlace()

    CreateFromDrafts_WriteBoundaryCollision_RestoresCallerOwnedRowDraftToItsPreCallState()

    CreateFromDrafts(IReadOnlyList<SerenityWizardScratchDraft<PrefetchPolicyProfile>>, string, string, bool, UnboundRowWriteMode) trims draft.Id directly on the CALLER-OWNED row draft — the same live, long-lived instance CreatePrefetchPolicyWindow keeps bound to its row list — before a write-boundary collision on that row's planned path refuses the whole run. Proves the live row draft serializes back to exactly its pre-call state on failure.

    Declaration
    public void CreateFromDrafts_WriteBoundaryCollision_RestoresCallerOwnedRowDraftToItsPreCallState()

    Create_DefaultProfileNotFirstInInput_ReordersRegistryProfilesWithDefaultFirst()

    The installer's boot wiring (UnitySerenityInstaller.InstallAssetPrefetcher) always uses the FIRST entry of the registry's Profiles array, not GetDefault() — so Create() must guarantee the 'default' profile lands first regardless of the authored row order.

    Declaration
    public void Create_DefaultProfileNotFirstInInput_ReordersRegistryProfilesWithDefaultFirst()

    Create_ExistingAssetSeededOnEverySerializedField_RestoresThemAll()

    Field-by-field regression guard for the generic capture Create(Input) now rolls back an already-existing profile through (transaction.TrackExistingAsset), replacing the hand-rolled ExistingProfileSnapshot this migration deleted. Writes a distinct value into EVERY serialized field of the real profile asset via reflection — not just the ones ApplyProfileFields happens to know about — forces the update run to fail via the same planted-and-deleted-file technique Create_ExistingProfileFileRemovedBeforeSecondRun_RollsBackTheAppliedFieldMutation() uses, and asserts every field comes back. Proves the capture covers whatever the type declares, not a hand-maintained field list a future field addition could silently outgrow.

    Declaration
    public void Create_ExistingAssetSeededOnEverySerializedField_RestoresThemAll()

    Create_ExistingProfileFileRemovedBeforeSecondRun_RollsBackTheAppliedFieldMutation()

    The symmetric counterpart of Create_ExistingRegistryFileRemovedBeforeSecondRun_RollsBackTheAppliedProfileMutation(): an already-existing PROFILE asset (not the registry) has its file removed from disk between runs. The same disk-existence guard the registry branch has must also apply to profiles, and a failure there must roll back the in-memory field mutation just applied to it.

    Declaration
    public void Create_ExistingProfileFileRemovedBeforeSecondRun_RollsBackTheAppliedFieldMutation()

    Create_ExistingProfileWithEmptyIdMatchingRowByAssetName_UpdatesInPlaceInsteadOfDuplicating()

    The folder search that decides update-in-place vs create-new must also match by resolved id: before that, an empty-Id asset named after the row was not found, and the "new" asset path collided with the existing file — silently clobbering it via CreateAsset.

    Declaration
    public void Create_ExistingProfileWithEmptyIdMatchingRowByAssetName_UpdatesInPlaceInsteadOfDuplicating()

    Create_ExistingProfileWithSameIdInOutputFolder_UpdatesInPlaceInsteadOfDuplicating()

    Declaration
    public void Create_ExistingProfileWithSameIdInOutputFolder_UpdatesInPlaceInsteadOfDuplicating()

    Create_ExistingRegistryAtExplicitPath_UpdatesItInPlaceInsteadOfCreatingANewOne()

    Declaration
    public void Create_ExistingRegistryAtExplicitPath_UpdatesItInPlaceInsteadOfCreatingANewOne()

    Create_ExistingRegistryFileRemovedBeforeSecondRun_RollsBackTheAppliedProfileMutation()

    Exercises the resolve-or-edit rollback path specifically: an already-existing registry's Profiles gets its referenced profile mutated in memory, then the pre-persist disk check fails (the registry file was removed from disk after the first Create() call). The mutation must be rolled back to its pre-call values, not just reported as failed.

    Declaration
    public void Create_ExistingRegistryFileRemovedBeforeSecondRun_RollsBackTheAppliedProfileMutation()

    Create_NewRegistry_WritesTheIdTheWizardShowsItBy_ItsFileName()

    Declaration
    public void Create_NewRegistry_WritesTheIdTheWizardShowsItBy_ItsFileName()

    Create_RegisterAsAddressablesFalse_LeavesAddressablesUntouched()

    Declaration
    public void Create_RegisterAsAddressablesFalse_LeavesAddressablesUntouched()

    Create_RegisterAsAddressablesTrue_RegistersTheRegistryAndEveryProfile()

    Declaration
    public void Create_RegisterAsAddressablesTrue_RegistersTheRegistryAndEveryProfile()

    Create_RegistryFileRemovedAfterFreshProfileIsReferencedLive_StillDeletesTheFreshProfile()

    Reproduces the interception bug the transaction's existing-asset restores fix. Create assigns registryAsset.Profiles a live reference to the freshly created 'default' profile before the ALREADY-EXISTING registry's own disk-verification runs (a planted-then-deleted registry file forces that verification to fail) — mirrors PlayerInputCreatorTests.Create_Multiplayer_DefinitionFileRemovedOnFreshChain_RollsBackAllThreeFreshSubAssets's boundary shape. Serenity.Global.Infrastructure.Editor.SerenityAssetCreationTransaction.Rollback replays the registry's tracked existing-asset restore FIRST — reverting Profiles to its pre-call value and dropping the live reference — before it ever deletes the freshly created profile, so SerenitySafeDeleteAssetProcessor.OnWillDeleteAsset's live UnityEditor.SerializedObject referencer scan never finds the registry still pointing at it. This test would have failed before that ordering existed.

    Declaration
    public void Create_RegistryFileRemovedAfterFreshProfileIsReferencedLive_StillDeletesTheFreshProfile()

    Create_RegistryWriteFailsAfterProfilesAlreadyCreated_RollsBackEverythingIncludingAddressablesEntries()

    Exercises a genuine rollback: two profiles and their Addressables entries — plus the nested output folder chain — are actually written to disk and registered before the registry write fails, forcing a full rollback. Asserts every one of those real artifacts is gone afterward, not just that the call reported failure.

    Declaration
    public void Create_RegistryWriteFailsAfterProfilesAlreadyCreated_RollsBackEverythingIncludingAddressablesEntries()

    Create_UpdateRunAgainstAnExistingRegistry_DoesNotCreateTheOutputFolder()

    Regression guard for the switch away from an unconditional, always-run EnsureProjectFolderExists on ProfilesFolderPath ahead of resolution, to the registry (ResolveOrCreateAtPath<TSettings>(SettingsResolution<TSettings>, string, string, string, SerenityAssetCreationTransaction, out bool)) and every profile (ResolveOrCreateByIdInFolder<TSettings>(string, string, string, SerenityAssetCreationTransaction, out bool)) each ensuring their own folder lazily, only on their own create path. A second run against an already fully-resolved registry and profile — nothing left to create — must create nothing new, and must never touch any folder outside the two it was actually given.

    Declaration
    public void Create_UpdateRunAgainstAnExistingRegistry_DoesNotCreateTheOutputFolder()

    Create_UpdatingARegistryWithABlankId_FillsItInWithItsFileName()

    Declaration
    public void Create_UpdatingARegistryWithABlankId_FillsItInWithItsFileName()

    Create_UpdatingARegistryWithAnAuthoredId_KeepsIt()

    Declaration
    public void Create_UpdatingARegistryWithAnAuthoredId_KeepsIt()

    Create_ValidInput_CreatesRegistryAndProfileWithConfiguredValues()

    Declaration
    public void Create_ValidInput_CreatesRegistryAndProfileWithConfiguredValues()

    OneTimeTearDown()

    Declaration
    public void OneTimeTearDown()

    Preview_ExistingRegistryHasLeftoverProfileFromPreviousRun_ListsItInProfilesToUnlink()

    Declaration
    public void Preview_ExistingRegistryHasLeftoverProfileFromPreviousRun_ListsItInProfilesToUnlink()

    Preview_ExistingRegistryProfileWithEmptyIdMatchingRowByAssetName_ListsNothingToUnlink()

    Declaration
    public void Preview_ExistingRegistryProfileWithEmptyIdMatchingRowByAssetName_ListsNothingToUnlink()

    SetUp()

    Declaration
    public void SetUp()

    TearDown()

    Declaration
    public void TearDown()

    Validate_DefaultProfileNotFirstInRowOrder_WarnsAboutReordering()

    Declaration
    public void Validate_DefaultProfileNotFirstInRowOrder_WarnsAboutReordering()

    Validate_DuplicateProfileIdsDifferingOnlyByCase_ReturnsError()

    Declaration
    public void Validate_DuplicateProfileIdsDifferingOnlyByCase_ReturnsError()

    Validate_DuplicateProfileIds_ReturnsError()

    Declaration
    public void Validate_DuplicateProfileIds_ReturnsError()

    Validate_EmptyProfileList_ReturnsError()

    Declaration
    public void Validate_EmptyProfileList_ReturnsError()

    Validate_ExistingRegistryHasLeftoverProfileFromPreviousRun_WarnsAboutUnlinking()

    Re-running the wizard against an existing registry with fewer rows than before drops the leftover profile from the registry's array (its asset stays on disk, untouched). Validate/Preview must warn about this so it is never a silent surprise.

    Declaration
    public void Validate_ExistingRegistryHasLeftoverProfileFromPreviousRun_WarnsAboutUnlinking()

    Validate_ExistingRegistryProfileWithEmptyIdMatchingRowByAssetName_DoesNotWarnAboutUnlinking()

    A referenced profile whose serialized Id is empty still resolves to its asset name at runtime (PrefetchPolicyRegistry.Get matches the resolved id) — the orphan check must classify by that same resolved identity, not the raw serialized field.

    Declaration
    public void Validate_ExistingRegistryProfileWithEmptyIdMatchingRowByAssetName_DoesNotWarnAboutUnlinking()

    Validate_MaxSizeMBBelowRuntimeFloor_WarnsWithoutBlocking()

    Declaration
    public void Validate_MaxSizeMBBelowRuntimeFloor_WarnsWithoutBlocking()

    Validate_MaxSizeMBZeroOrLess_ReturnsError()

    Declaration
    public void Validate_MaxSizeMBZeroOrLess_ReturnsError()

    Validate_NoDefaultProfile_ReturnsError()

    Declaration
    public void Validate_NoDefaultProfile_ReturnsError()

    Validate_OrphanedProfileWithEmptyId_WarnsUsingItsResolvedAssetName()

    Declaration
    public void Validate_OrphanedProfileWithEmptyId_WarnsUsingItsResolvedAssetName()

    Validate_ProfileIdCollidesWithAnAssetOutsideTheFolder_Warns()

    The shipped-Id-collision defect this change fixes: Serenity ships its own PrefetchPolicyProfile.asset with Id 'default' under Assets/Serenity/Media/AssetPrefetcher, entirely outside a project's own output folder — so nothing on Configure/Summary/Done ever mentioned that a project author's own 'default' profile shares an Id with the shipped one. Validate must now warn (never block) whenever an authored row's Id collides with a PrefetchPolicyProfile living outside the target folder, naming the colliding asset's path.

    Declaration
    public void Validate_ProfileIdCollidesWithAnAssetOutsideTheFolder_Warns()
    In this article
    © 2026 Serenity. All Rights Reserved