logo SERENITY
Game Systems Foundation
Search Results for

    Show / Hide Table of Contents

    Class UpdateInPlaceRoundTripContract

    Reusable EditMode contract for the rule that an editor tool managing an existing asset owns everything that asset already has: confirming an update-in-place wizard run with ZERO edits must leave every already-set field on every asset it touches untouched — name, hide flags, and every serialized field alike. Guards against a wizard's own SeedFromTarget seeding only a handful of fields and silently reverting everything else to its static template default the moment Confirm is pressed.

    A wizard registers by implementing UpdateInPlaceRoundTripContract.IAdapter<TAsset> — seed from a SET of existing assets, confirm with no further edits — a single asset is the trivial case of a one-element set, for the multi-asset adopters (Wave Set, Player Input, Character) coming in later slices.

    Built entirely on the existing UnitySerializedFieldReflection / UnitySerializedFieldSeeds / UnitySerializedFieldAssertions trio (Serenity.Tests.Shared) rather than a new UnityEditor.SerializedObject-based walker of its own — the same machinery SystemConfigurationSettingsCreatorTests already exercises for its own field-walk regression test, so an Object-reference field is mutated (via MutateEveryFieldToNonDefault(Object, Object, string)'s alternateObjectReference) and compared by asset identity, never silently skipped.

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

    Methods

    AssertRoundTripsUnchanged<TAsset>(IAdapter<TAsset>, IReadOnlyList<TAsset>, IReadOnlyCollection<string>, IReadOnlyCollection<string>)

    Runs the contract: snapshots every asset in existingAssets (name, hide flags, every serialized field), seeds and confirms through adapter with zero edits, then asserts every one of them round-tripped unchanged.

    Declaration
    public static void AssertRoundTripsUnchanged<TAsset>(UpdateInPlaceRoundTripContract.IAdapter<TAsset> adapter, IReadOnlyList<TAsset> existingAssets, IReadOnlyCollection<string> actualUnmutatedFieldNames, IReadOnlyCollection<string> expectedUnmutatedFieldNames) where TAsset : Object
    Parameters
    Type Name Description
    UpdateInPlaceRoundTripContract.IAdapter<TAsset> adapter

    The wizard-specific seed/confirm seam.

    IReadOnlyList<TAsset> existingAssets

    The already-authored assets the run must leave untouched — built with MutateEveryFieldToNonDefault(Object, Object, string) so every field carries a value distinct from a freshly created asset's own defaults.

    IReadOnlyCollection<string> actualUnmutatedFieldNames

    The union of every path MutateEveryFieldToNonDefault(Object, Object, string) reported as unmutated while building existingAssets.

    IReadOnlyCollection<string> expectedUnmutatedFieldNames

    What the test deliberately acknowledges as fields the fixture builder cannot mutate generically (an Object-reference field with no compatible alternate, a composite kind deeper than Serenity.Tests.Shared.Editor.UpdateInPlaceRoundTripContract.MaxCompositeDepth). Must match actualUnmutatedFieldNames exactly — a field that starts going unmutated without the test being updated to expect it fails loudly here, never silently passes a vacuous round trip.

    Type Parameters
    Name Description
    TAsset

    MutateEveryFieldToNonDefault(Object, Object, string)

    Sets every field UnitySerializedFieldReflection can see on asset to a value distinct from what it currently holds — the "existing asset whose every field was set to a non-default value" fixture AssertRoundTripsUnchanged<TAsset>(IAdapter<TAsset>, IReadOnlyList<TAsset>, IReadOnlyCollection<string>, IReadOnlyCollection<string>) needs. Scalar and enum fields are seeded via SeedValueDistinctFrom(FieldInfo, object, string, Object); an Object-reference field (or an array of one) is seeded when alternateObjectReference is assignable to it; a nested struct field, or an array of one (e.g. a Sync Map row), is walked the same way one level down. Returns the dotted paths of every field this could not mutate — an Object-reference field with no compatible alternate, or a composite nested past Serenity.Tests.Shared.Editor.UpdateInPlaceRoundTripContract.MaxCompositeDepth — so a caller's own AssertRoundTripsUnchanged<TAsset>(IAdapter<TAsset>, IReadOnlyList<TAsset>, IReadOnlyCollection<string>, IReadOnlyCollection<string>) call must acknowledge them explicitly, never silently.

    Declaration
    public static IReadOnlyList<string> MutateEveryFieldToNonDefault(Object asset, Object alternateObjectReference = null, string seedSalt = "")
    Parameters
    Type Name Description
    Object asset

    The asset to mutate. Marked dirty; the caller saves.

    Object alternateObjectReference

    An asset (or in-memory instance) whose type mutates an Object-reference field with a real value instead of leaving it reported as unmutated.

    string seedSalt

    Distinguishes the SEED TEXT (never the reported field paths) between two calls that mutate different assets of the SAME type in the same test (e.g. two sibling wave configs) — without it, every string field's seed text is derived from the field's own dotted path alone, so two same-typed assets would seed an Id-shaped field to the exact same string and collide on the by-Id lookup the round trip depends on. Empty (the default) matches every other adopter, which mutates exactly one asset per type.

    Returns
    Type Description
    IReadOnlyList<string>
    In this article
    © 2026 Serenity. All Rights Reserved